I dont know about you, but I tried so hard on how to create a news ticker. I found approaches that would append what was off the screen on to the back side of it. This caused the animation to jump by a whole character. Then I found methods that would not loop until the text was completely off the screen. After asking a ton of questions on this I thought it would be nice if I just posted my final solution for all the people wondering how to make it. Here you go.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Scroll2 extends JPanel implements ActionListener{
private int x;
private int x2;
private int y;
private int width;
private String textIV;
private Color textColorIV;
private Color bGColorIV;
private Font f=(new Font("SansSerif", Font.BOLD,34));
Timer t;
// TODO font size
public Scroll2(String text, Color textColor, Color bGColor)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=0;
textIV=text;
this.setVisible(true);
Timer refreshTimer = new javax.swing.Timer(1000/50, this);
refreshTimer.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(textIV);
x = 0;
y = (int) (getHeight()*.6);
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(textIV);
}
g.setColor(bGColorIV);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(textColorIV);
g.drawString(textIV, x, y);
g.drawString(textIV, x2, y);
}
public void refresh ()
{
if(x>=(-width)&&x2!=Integer.MIN_VALUE)
{
x=x-2;
x2=x2-2;
}
if(x2<=-width&&x!=Integer.MIN_VALUE)
{
x2=width;
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
this.refresh();
}
}
Related
So I've been trying to make a sprite object (called heart) that can be moved around by players on the JPanel "Battle box". Very similar code has worked for me in the past but weirdly not now. I need to clarify, this is PART of a project, not the whole thing. The BattleBox JPanel object gets called from a different part of the program, though technically it's possible the error isn't within these classes
Also btw not all the code is written for the new ones since I need to make it appear first before I can start making it move around and such xD Anyway any help people can give is appreciated and, just in case it wasn't obvious, I'm self taught and bad so sorry if the answer's super obvious
Also slight side note, the clock also doesn't show but I've kinda accepted fate with that one for now
Battle screen class
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel {
private Heart heart;
private Thread thread;
private Clock clock;
public BattleScreen() {
this.setBounds(0, 0, MainMenu.frame.getWidth(), MainMenu.frame.getHeight());
this.setBackground(Color.black);
clock = new Clock(this);
heart = new Heart();
heart.allowMovement(true);
MainMenu.con.add(this);
this.repaint();
this.setVisible(true);
thread = new Thread() {
public void run() {
repaint();
}
};
thread.start();
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
heart.draw(g, this);
clock.draw(g, this);
}
}
Heart class
import java.awt.Component;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
int xPos, yPos, width, height;
Thread thread;
BufferedImage image;
public Heart() {
width = 44;
height = 44;
xPos = (MainMenu.MainMenu.con.getWidth() - width) / 2;
yPos = (MainMenu.MainMenu.con.getHeight() - height) / 2;
try {
image = ImageIO.read(new File("src/BattleScreen/heart.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.setBackground(Color.BLUE);
this.repaint();
thread = new Thread() {
public void run() {
updateMovement();
}};
}
void draw (Graphics g, Component c) {
g.drawImage(image, xPos, yPos, width, height, c);
}
}
Classes that worked in the past:
OLD BattleScreen
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel implements KeyListener {
private static JFrame frame = MainMenu.frame;
static Heart sprite = new Heart(frame.getWidth()/2, frame.getHeight()/2, 20, 20);
public BattleScreen() {
addKeyListener(this);
setFocusable(true);
setBounds(0, 0, frame.getWidth(), frame.getHeight());
setBackground(Color.black);
MainMenu.con.add(this);
new Thread(() -> {
while (true) {
sprite.updateMovement();
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
sprite.draw(g, this);
}
}
OLD Heart class
package DEADBattleScreen;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
Image img;
int xPos, yPos, width, height;
Thread thread;
Heart(int x, int y, int w, int h) {
this.xPos = x;
this.yPos = y;
this.width = w;
this.height = h;
try {
img = ImageIO.read(new File("src/battlescreen/heart.png"));
} catch (IOException e ) {
e.printStackTrace();
}
}
void draw (Graphics g, Component c) {
g.drawImage(img, xPos, yPos, width, height, c);
}
}
So this is my main class:
package testgame;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Game extends JFrame {
public static JFrame frame = new JFrame("Just a test!");
public static void LoadUI() {
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(550, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true); }
public static void main(String[] args) {
LoadUI();
frame.add(new Circles());
}
}
And this is the class that handles what I want to paint:
package testgame;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circles extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawBubbles(g); }
public void drawBubbles(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh
= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
int x, y, size;
x = (int) (Math.random() * 500) + 15;
y = (int) (Math.random() * 450) + 15;
size = (int) (Math.random() * 50) + 25;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, size, size);
g2d.fillOval(x, y, size, size); }
}
If I add another
frame.add(new Circles());
Nothing happens. I think it has to do with the layout of the frame, but the coordinates of the bubbles are random so I'm not sure how to work with this.
In this case I'm using a fixed-size array of 5, you may change it to a bigger fixed-size array or an ArrayList, as shown in this answer
For your particular case I would create a Circle class that may contain the data for each circle, being the coords and the size
Then create a CirclePane class that would paint all the Circles in a single paintComponent() method.
And finally, the Main class that would have a JFrame that may contain the CirclePane added to it.
With the above tips in mind, you could end up with something like this:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CircleDrawer {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleDrawer()::createAndShowGui); //We place our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
CirclePane circle = new CirclePane(5); //We want to create 5 circles, we may want to add more so we change it to 10, or whatever number we want
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Data class
class Circle {
private Point coords;
private int size;
public Circle(Point coords, int size) {
this.coords = coords;
this.size = size;
}
public Point getCoords() {
return coords;
}
public void setCoords(Point coords) {
this.coords = coords;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
//The drawing class
#SuppressWarnings("serial")
class CirclePane extends JPanel {
private int numberOfCircles;
private Circle[] circles;
public CirclePane(int numberOfCircles) {
this.numberOfCircles = numberOfCircles;
circles = new Circle[numberOfCircles];
for (int i = 0; i < numberOfCircles; i++) {
Point coords = new Point((int) (Math.random() * 500) + 15, (int) (Math.random() * 450) + 15); //We generate random coords
int size = (int) (Math.random() * 50) + 25; //And random sizes
circles[i] = new Circle(coords, size); //Finally we create a new Circle with these properties
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < numberOfCircles; i++) {
g2d.draw(new Ellipse2D.Double(circles[i].getCoords().getX(), circles[i].getCoords().getY(), circles[i].getSize(), circles[i].getSize())); //We iterate over each circle in the array and paint it according to its coords and sizes
}
}
#Override
public Dimension getPreferredSize() { //Never call JFrame.setSize(), instead override this method and call JFrame.pack()
return new Dimension(500, 500);
}
}
}
Which produces a similar output to this:
I hope this helps you to get a better idea, read about the MVC pattern as I made use of it for this answer.
Note:
In this answer I used the Shapes API, according to the recommendation of #MadProgrammer in this other answer. I used it in the g2d.draw(...) line.
For a deeper understanding in how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.
Basically I have a (width x height) size of pixels in a buffered image and would like to draw simple horizontal BLACK lines, one after another into the Buffered Image / JPanel. Each click of the button should create a new horizontal line.
Although the button event reaches the updateImage() function, AND the Log output indicates that paintComponent(Graphics g) is called by the system. There is no update happening. The only time a Line is drawn is on the constructor initialisation. Nothing is being updated to the screen on each button click; only the initial one line is drawn from a test call in the constructor.
The end product is to simulate a printer.
Thank You.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class ViewPaper extends JPanel {
private BufferedImage buffer = null;
private final int width = 384;
private int height = 500;
private final static Logger LOG = Logger.getLogger(ViewPaper.class
.getName());
public ViewPaper() {
super();
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
printDotLine(50);
LOG.log(Level.INFO, "ViewPaper: Load Successful.");
}
/* Update BufferedImage then repaint to screen.
* Note. A Top Level repaint() is also called
* and according to log the paintComponent is
* called but nothing is happening!
*/
public void updateImage(int y) {
printDotLine(y);
repaint();
LOG.log(Level.INFO, "ViewPaper: updateImage(); Called.");
}
/* Update JPanel with updated buffer. */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(buffer, 0, 0, this);
g2d.dispose();
LOG.log(Level.INFO, "ViewPaper: paintComponent(); Called.");
}
/* Sets row of pixels in buffer to black.*/
public void printDotLine(int y) {
for (int x = 0; x < width; x++) {
buffer.setRGB(x, y, Color.BLACK.getRGB());
}
LOG.log(Level.INFO, "ViewPaper: printDotLine(); Called.");
}
}
You almost answered your own question but forgot to include actual button.
Here is how it should be (almost - no static should be used -it's just simple example/improvement of your code):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ViewPaper extends JPanel {
static ViewPaper viewPaper;
static int line=50;
public static void main(String args[])
{
JFrame frame=new JFrame("View Paper");
viewPaper=new ViewPaper();
frame.add(viewPaper);
JButton button=new JButton("Click Me!");
frame.add(button,BorderLayout.SOUTH);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
viewPaper.updateImage(line++);
}
});
frame.setSize(300,500);
frame.setVisible(true);
}
private BufferedImage buffer = null;
private final int width = 384;
private int height = 500;
private final static Logger LOG = Logger.getLogger(ViewPaper.class
.getName());
public ViewPaper() {
super();
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
printDotLine(50);
LOG.log(Level.INFO, "ViewPaper: Load Successful.");
}
/* Update BufferedImage then repaint to screen.
* Note. A Top Level repaint() is also called
* and according to log the paintComponent is
* called but nothing is happening!
*/
public void updateImage(int y) {
printDotLine(y);
repaint();
}
/* Update JPanel with updated buffer. */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(buffer, 0, 0, this);
g2d.dispose();
LOG.log(Level.INFO, "ViewPaper: paintComponent(); Called.");
}
/* Sets row of pixels in buffer to black.*/
public void printDotLine(int y) {
for (int x = 0; x < width; x++) {
buffer.setRGB(x, y, Color.BLACK.getRGB());
}
LOG.log(Level.INFO, "ViewPaper: Update Received.");
}
}
I have finally made a ticker that functions how I want it to, but the only problem now is that it seems kind of shaky and looks like it is being displayed on an old tv. How do I make it look smoother?
Here is my code:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Scroll1 extends JPanel{
private int x;
private int x2;
private int y;
private int width;
private String text;
private Font f=(new Font("SansSerif", Font.PLAIN,24));
public Scroll1(int startX)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=150;
text= "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK. ";
}
#Override
public void paintComponent(Graphics g)
{
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(text);
x = 0;
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(text);
}
g.setColor(Color.white);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(Color.black);
g.drawString(text, x, y);
g.drawString(text, x2, y);
}
public void start() throws InterruptedException{
while(true){
while(x>= (-width)&&x2!=Integer.MIN_VALUE){
x--;
x2--;
y = getHeight()/2;
Thread.sleep(15);
this.
validate();
repaint();
if(x2<=-width)
{
x2=width;
}
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll1 scrolling = new Scroll1(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
try {
scrolling.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Call super.paintComponent before you perform custom painting
Prefer Swing Timer of a Thread, as Swing Timers are triggered within the EDT and you run less risk of having dirty reads between the paintComponent method and the Thread
paintComponent really shouldn't be making decisions about the rendering state, instead it should just paint the current state.
You don't need g.fillRect(this.getX(), this.getY(), (int) this.getBounds().getWidth(), (int) this.getBounds().getHeight());, just set the background color of the panel and allow the painting routines to do their jobs. Painting is done from 0x0, been the top/left corner of the component
There is an issue with your while loop in your start method which doesn't seem to allow it to run (at least in my testing)
Can you help me try to transfer it over to a swing timer or give me a good source to do so?
Can you pay me? ;)
First, getting it to be completely "stagger" free isn't going to be possible, as the process is reliant on the system's capability of keeping up to date with your requests. For example, you really don't need to it be running at > 60fps, seriously, 25fps or even 15fps would be more the suitable for what you are trying to do. You control the speed of the scroll by using the movement delta instead
The following example uses a Swing Timer, as did my previous example, but this example will also create a "continuous" loop, where if the amount of text been displayed is less then the width of the visible area, it will add a "new" message to trail it.
This example would be capable of display multiple different messages of varying lengths but I guess, that's a bonus
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
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.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Scroll1 extends JPanel {
private String text;
private Font f = (new Font("SansSerif", Font.PLAIN, 24));
private List<TickerTap> tickerTaps;
public Scroll1(int startX) {
text = "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK.";
setFont(f);
setBackground(Color.WHITE);
tickerTaps = new ArrayList<>(4);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FontMetrics fm = getFontMetrics(getFont());
if (tickerTaps.isEmpty()) {
tickerTaps.add(new TickerTap(text, getWidth()));
}
TickerTap taps[] = tickerTaps.toArray(new TickerTap[tickerTaps.size()]);
int visibleWidth = 0;
for (TickerTap tap : taps) {
tap.move();
int messageWidth = fm.stringWidth(tap.getMessage());
if (tap.getxPos() <= -messageWidth) {
tickerTaps.remove(tap);
} else {
visibleWidth += tap.getxPos() + messageWidth;
}
}
while (visibleWidth < getWidth()) {
TickerTap last = tickerTaps.get(tickerTaps.size() - 1);
int xPos = last.getxPos() + fm.stringWidth(last.getMessage());
String message = " * " + text;
TickerTap next = new TickerTap(message, xPos);
visibleWidth += next.getxPos() + fm.stringWidth(next.getMessage());
tickerTaps.add(next);
}
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
for (TickerTap tap : tickerTaps) {
FontMetrics fm = g2d.getFontMetrics();
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.black);
g2d.drawString(tap.getMessage(), tap.getxPos(), y);
}
}
public class TickerTap {
private String message;
private int xPos;
public TickerTap(String message, int xPos) {
this.message = message;
this.xPos = xPos;
}
public String getMessage() {
return message;
}
public int getxPos() {
return xPos;
}
public void move() {
xPos -= 2;
}
}
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();
}
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll1 scrolling = new Scroll1(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
}
});
}
}
Take a look at How to use Swing Timers for more details. Timer is actually really to understand if you simply think of it like a kind of loop
Welp Mad Programmer you are a boss, thanks for all the help. I running what you had and it didnt work, strange huh? Anyway here is what I got now that seems to be working a whole lot smoother but still a few glitches here and there:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Scroll2 extends JPanel implements ActionListener{
private int x;
private int x2;
private int y;
private int width;
private String text;
private Font f=(new Font("SansSerif", Font.BOLD,34));
Timer t;
public Scroll2(int startX)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=150;
text= "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK. ";
Timer refreshTimer = new javax.swing.Timer(10, this);
refreshTimer.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(text);
x = 0;
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(text);
}
g.setColor(Color.white);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(Color.black);
g.drawString(text, x, y);
g.drawString(text, x2, y);
}
public void refresh ()
{
if(x>=(-width)&&x2!=Integer.MIN_VALUE)
{
x--;
x2--;
y = getHeight()/2;
}
if(x2<=-width&&x!=Integer.MIN_VALUE)
{
x2=width;
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll2 scrolling = new Scroll2(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
this.refresh();
}
}
Graphics2D animation, cant understand why paintComponent only draws the shape one time and then stops. I Also can't understand why my timer counter variable isn't incrementing. I've been banging my head against this for over an hour, can anyone nudge me in the right direction?
Tetri
package Tetris;
import javax.swing.JLabel;
import java.util.ArrayList;
public class Tetri {
private int xCoords= (int)(Math.random()*10);
private int yCoords=0;
private int shapeType;
private int orientation=0;
public Tetri(int shapeT){
shapeType = shapeT;
}
public int getOrient(){
return orientation;
}
public void setOrient(int orient){
orientation = orient;
}
public void setXCoords(int coords){
xCoords = coords;
}
public int getXCoords(){
return xCoords;
}
public void setYCoords(int coords){
yCoords = coords;
}
public int getYCoords(){
return yCoords;
}
public int getShape(){
return shapeType;
}
}
MovingRectangle
package Tetris;
import javax.swing.JFrame;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
public class MovingRectangle {
public static void main(String[] args){
new MovingRectangle();
}
public MovingRectangle() {
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
JFrame frame = new JFrame("Tetris");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0,1));
frame.add(new TestPane(Color.RED));
frame.setSize(1000,800);
frame.setVisible(true);
}
});
}
TestPane
public class TestPane extends JPanel {
private Graphics g0;
private int unit = 50;
private ArrayList<Tetri> shapeList = new ArrayList<Tetri>();
int timercount =1;
public TestPane(Color foreground){
setForeground(foreground);
this.setBackground(Color.BLUE);
Timer timer = new Timer(2000,new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
System.out.println("timercount :"+timercount);
timercount = timercount++;
shapeList.add(new Tetri((int)(Math.random()*2)));
System.out.println("shapeList size : "+shapeList.size());
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g){
g0 = g;
super.paintComponent(g);
if(shapeList.size()>0){
Tetri current = shapeList.get(0);
if(current.getShape()==0){
createShape0(current.getXCoords(),current.getYCoords(),current.getOrient());
}
if(current.getShape()==1){
createShape1(current.getXCoords(),current.getYCoords(),current.getOrient());
}
current.setYCoords(current.getYCoords()+50);
}
else{shapeList.add(new Tetri((int)(Math.random()*2)));}
}
public void createShape0(int xc,int yc,int orien){
int yPixel= yc*50;
int xPixel= xc*50;
Graphics2D g2d = (Graphics2D) g0.create();
g2d.setColor(Color.RED);
if(orien==0){
g2d.drawRect(xPixel, yPixel, unit*4, unit);
}
if(orien==1){
g2d.drawRect(xPixel, yPixel, unit, unit*4);
}
}
public void createShape1(int xc,int yc, int orien){
int yPixel= yc*50;
int xPixel= xc*50;
Graphics2D g2d = (Graphics2D) g0.create();
g2d.setColor(Color.GREEN);
if(orien==0){
g2d.drawRect(xPixel, yPixel, unit*3, unit);
g2d.drawRect(xPixel+50, yPixel+50,unit,unit);
}
if(orien==1){
g2d.drawRect(xPixel+50, yPixel-50, unit, unit*3);
g2d.drawRect(xPixel, yPixel,unit,unit);
}
if(orien==2){
g2d.drawRect(xPixel, yPixel, unit*3, unit);
g2d.drawRect(xPixel+50, yPixel-50,unit,unit);
}
if(orien==3){
g2d.drawRect(xPixel+50, yPixel-50, unit, unit*3);
g2d.drawRect(xPixel+100, yPixel,unit,unit);
}
}
}
}
I have a list of things, but lets start with the most obvious.
You "movement" calculations are way off.
int yPixel = yc * 50;
int xPixel = xc * 50;
This is causing your shape to "jump" expectational larger distances...in fact, I can't find any reason for you to "modify" these values at all. Simply making them read as...
int yPixel = yc;
int xPixel = xc;
Got the shape moving just fine...
Code Review
Don't maintain any references to any graphics context you didn't create. These references can be shared amongst the other UI components and there is no guarantee that the reference is in one paint cycle, will be the same on the next. If you want to paint to a graphics context, pass it as parameter to the method that needs to do the painting.
If you create a graphics context (Graphics2D g2d = (Graphics2D) g.create();) then you are responsible for disposing of it (g2d.dispose()) when you are done with it. Otherwise you will slowly consume system resources until your application crashes (talking from experience - it's a hard bug to find ;))
You seem to be going about the process the hard way. You should create a class for each Shape, based off the Tetri that knows how to "draw" itself. That way you would simple pass the graphics context to it in your paint loop and not care. This makes is significantly easier to add new pieces over time.
If you base each of these pieces off a java.awt.Shape, it becomes significantly easier to transform the shape and rotate them.
Take a look at 2D Graphics, in particular, Working with Geometry