I'm trying for more than 2 days to implement a specific requirement for a text editor window... unfortunately without success so far :(
The goal is to get a text editor window which will highlight the current row, like other text editors do. With current row I mean the row where currently the cursor/caret is positioned.
I already found two different approaches but unfortunately I'm not able to adopt them so they work as expected.
The first approach is to overwrite the DefaultHighlighter (http://snippets.dzone.com/posts/show/6688).
In the second approach the HighlighterPainter will be overwritten instead (http://www.jroller.com/santhosh/date/20050622).
Right now I'm trying to adopt the first approach in my project but as I said it is not working as desired.
At the end of this post I'm posting a small sample application which demonstrates the problem.
If I start the program, the caret is placed at the beginning of the first line. However, the line is not highlighted.
Now I type in some characters. Those chars will be highlighted but only those chars not the complete line
I hit enter to move to the next line. The first line is not highlighted anymore what is correct. The second line isn't highlighted as well, what is not correct. Again, when I type in some chars, those will be higlighted but not the complete row.
When I now move back the caret to the first line, either by cursor up key or mouse clicking, the complete first line will be highlighted, not only the existing chars. This is the behavior I want right from the start.
I hope anybody can tell me what I'm doing wrong here... or explain why it is not possible to resolve that issue at all. Any alternative solutions how I could realize the line highlighting are also highly appreciated!
Thanks a lot in advance
Cheers
Preachie
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
public class HighlightProblem extends JFrame {
private static final long serialVersionUID = 1L;
private final JTextPane textPane;
private final Highlighter.HighlightPainter cyanPainter;
public HighlightProblem() {
cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.CYAN);
textPane = new JTextPane();
textPane.setPreferredSize(new Dimension(500, 300));
textPane.setHighlighter(new LineHighlighter());
textPane.addCaretListener(new CaretListener() {
#Override
public void caretUpdate(CaretEvent e) {
setHighlight(e);
}
});
getContentPane().add(textPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
new HighlightProblem();
}
public void setHighlight(CaretEvent e) {
textPane.getHighlighter().removeAllHighlights();
int currentLine = getLineFromOffset(textPane, e.getDot());
int startPos = getLineStartOffsetForLine(textPane, currentLine);
int endOffset = getLineEndOffsetForLine(textPane, currentLine);
try {
textPane.getHighlighter().addHighlight(startPos, endOffset, cyanPainter);
} catch (Exception ex) {
ex.printStackTrace();
}
textPane.repaint();
}
public int getLineFromOffset(JTextComponent component, int offset) {
return component.getDocument().getDefaultRootElement().getElementIndex(offset);
}
public int getLineStartOffsetForLine(JTextComponent component, int line) {
return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
}
public int getLineEndOffsetForLine(JTextComponent component, int line) {
return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
}
public class LineHighlighter extends DefaultHighlighter {
private JTextComponent component;
#Override
public final void install(final JTextComponent c) {
super.install(c);
this.component = c;
}
#Override
public final void deinstall(final JTextComponent c) {
super.deinstall(c);
this.component = null;
}
#Override
public final void paint(final Graphics g) {
final Highlighter.Highlight[] highlights = getHighlights();
final int len = highlights.length;
for (int i = 0; i < len; i++) {
Highlighter.Highlight info = highlights[i];
if (info.getClass().getName().indexOf("LayeredHighlightInfo") > -1) {
// Avoid allocing unless we need it.
final Rectangle a = this.component.getBounds();
final Insets insets = this.component.getInsets();
a.x = insets.left;
a.y = insets.top;
// a.width -= insets.left + insets.right + 100;
a.height -= insets.top + insets.bottom;
final Highlighter.HighlightPainter p = info.getPainter();
p.paint(g, info.getStartOffset(), info.getEndOffset(), a, this.component);
}
}
}
#Override
public void removeAllHighlights() {
textPane.repaint(0, 0, textPane.getWidth(), textPane.getHeight());
super.removeAllHighlights();
}
}
}
http://tips4java.wordpress.com/2008/10/29/line-painter/
I think this is what you are looking for. I took that LinePainter class and copied your constructor over into a main method, took out your highlighter parts and added a new LinePainter(textPane); Works like a charm
Below is the code to extract text from current line.
You can use same logic to get required indexes and highlight text
private String getCurrentEditLine() {
int readBackChars = 100;
int caretPosition = scriptEditor.getCaretPosition();
if (caretPosition == 0) {
return null;
}
StyledDocument doc = scriptEditor.getStyledDocument();
int offset = caretPosition <= readBackChars ? 0 : caretPosition
- readBackChars;
String text = null;
try {
text = doc.getText(offset, caretPosition);
} catch (BadLocationException e) {
}
if (text != null) {
int idx = text.lastIndexOf("\n");
if(idx != -1) {
return text.substring(idx);
}else {
return text;
}
}
return null;
}
I think this might be difficult to achieve using highlighters - I don't think it is what they were designed for. You may need to do it with custom painting code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
public class HighlightLineTest {
private static class HighlightLineTextPane extends JTextPane {
public HighlightLineTextPane() {
// Has to be marked as transparent so the background is not replaced by
// super.paintComponent(g);
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
try {
Rectangle rect = modelToView(getCaretPosition());
if (rect != null) {
g.setColor(Color.CYAN);
g.fillRect(0, rect.y, getWidth(), rect.height);
}
} catch (BadLocationException e) {
}
super.paintComponent(g);
}
#Override
public void repaint(long tm, int x, int y, int width, int height) {
// This forces repaints to repaint the entire TextPane.
super.repaint(tm, 0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Highlight test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new HighlightLineTextPane());
frame.setBounds(100, 100, 300, 400);
frame.setVisible(true);
}
}
Related
I'm new to Java, and I was assigned in my class to develop a code for the following question, I was only able to do the design, after that I didn't know how to continue adding the actions to every button.
This is the question:
https://www.chegg.com/homework-help/questions-and-answers/write-java-application-creates-frame-similar-one-shown--four-letter-word-shown-four-panels-q52352988
If anyone has ever solved it, please share it.
Thanks for help in advance!
Since this is homework, I'm not providing the entire code. I will provide snippets.
Here's the GUI I created. I wish I could show it as an animated GIF.
I added a stop button to stop the word rotation.
I wrote the code by breaking the problem down into smaller and smaller steps, then coding each of the steps. I ran many tests of the GUI before I finished it. Some of the tests failed, and I had to revise the code.
I wrote 6 classes. The main class created the JFrame, the letter panel group, and the control panel on the bottom. I wrote a LetterPanel class to create one letter panel. I wrote 3 actionListener classes, one for the JComboBox, one for the rotate button, and one for the stop button. I wrote an Animation class that rotates the letters every second.
Here are the colors I used to get the 4 shades of green.
Color[] colors = { new Color(50, 117, 1),
new Color(65, 159, 0), new Color(88, 201, 5),
new Color(107, 242, 2)
};
Setting up the main JPanel to hold the 4 LetterPanel objects was a bit tricky. Here's how I did it.
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
The LetterPanel class extended a JPanel and overrode the paintComponent method. First, I called the super.paintComponent method. Always call the super.paintComponent method first. Then, I painted the background color. Then, I painted the letter.
To paint the letter in each LetterPanel, I used the following code.
/**
* Draw a String centered in the middle of the panel.
*
* #param g2d The Graphics2D instance.
* #param text The String to draw.
* #param font The Font to draw with.
*/
public void drawCenteredString(Graphics2D g2d,
String text, Font font) {
FontMetrics metrics = g2d.getFontMetrics(font);
int x = (getWidth() - metrics.stringWidth(text)) / 2;
int y = ((getHeight() - metrics.getHeight()) / 2) +
metrics.getAscent();
g2d.setFont(font);
g2d.drawString(text, x, y);
}
The JComboBox actionListener gets the selected word from the JComboBox. The Oracle tutorial, How to Use Combo Boxes, tells you exactly how I set up the word JComboBox.
The rotate button actionListener checks if both JCheckBox fields are checked. Then it checks if neither JCheckBox field is checked. Finally, it starts an Animation thread.
The stop button stops the Animation thread.
The Animation thread rotates the word and pauses 1 second to allow you to see the rotation.
Here is the run loop.
#Override
public void run() {
while (running) {
updatePanel();
sleep(1000L);
if (leftSelected) {
word = rotateLeft(word);
} else {
word = rotateRight(word);
}
}
}
Here are my rotation methods.
private String rotateLeft(String word) {
return word.substring(1) + word.substring(0, 1);
}
private String rotateRight(String word) {
return word.substring(word.length() - 1) +
word.substring(0, word.length() - 1);
}
Edited to add;
I'd forgotten I'd answered this question. Enough time has passed so I'll post the entire application. I made the additional classes inner classes so I can post this code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RotateWord implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateWord());
}
private Animation animation;
private JCheckBox leftBox;
private JCheckBox rightBox;
private JComboBox<String> wordComboBox;
private JFrame frame;
private LetterPanel[] letterPanel;
private String word;
public RotateWord() {
this.word = "WORD";
}
#Override
public void run() {
frame = new JFrame("Rotate Word");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createWordPanel(word), BorderLayout.CENTER);
frame.add(createControlPanel(word),
BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createWordPanel(String word) {
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Color[] colors = { new Color(50, 117, 1),
new Color(65, 159, 0), new Color(88, 201, 5),
new Color(107, 242, 2)
};
letterPanel = new LetterPanel[word.length()];
for (int i = 0; i < word.length(); i++) {
letterPanel[i] = new LetterPanel(colors[i],
word.charAt(i));
panel.add(letterPanel[i]);
}
return panel;
}
public void updateWordPanel(String word) {
for (int i = 0; i < word.length(); i++) {
letterPanel[i].setLetter(word.charAt(i));
letterPanel[i].repaint();
}
}
private JPanel createControlPanel(String word) {
JPanel panel = new JPanel();
String[] words = { "ABLE", "BATH", "EXIT", "WORD" };
wordComboBox = new JComboBox<>(words);
wordComboBox.setSelectedItem(word);
wordComboBox.addActionListener(new WordListener());
panel.add(wordComboBox);
leftBox = new JCheckBox("Left");
panel.add(leftBox);
rightBox = new JCheckBox("Right");
panel.add(rightBox);
JButton rotateButton = new JButton("Rotate");
rotateButton.addActionListener(new RotateListener());
panel.add(rotateButton);
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
panel.add(stopButton);
return panel;
}
public class LetterPanel extends JPanel {
private static final long serialVersionUID = 1L;
private char letter;
private Color backgroundColor;
private Font font;
public LetterPanel(Color backgroundColor, char letter) {
this.backgroundColor = backgroundColor;
this.letter = letter;
this.font = getFont().deriveFont(96f)
.deriveFont(Font.BOLD);
this.setPreferredSize(new Dimension(120, 200));
}
public void setLetter(char letter) {
this.letter = letter;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(backgroundColor);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.BLACK);
drawCenteredString(g2d, Character.toString(letter),
font);
}
/**
* Draw a String centered in the middle of the panel.
*
* #param g2d The Graphics2D instance.
* #param text The String to draw.
* #param font The Font to draw with.
*/
public void drawCenteredString(Graphics2D g2d,
String text, Font font) {
FontMetrics metrics = g2d.getFontMetrics(font);
int x = (getWidth() - metrics.stringWidth(text)) / 2;
int y = ((getHeight() - metrics.getHeight()) / 2) +
metrics.getAscent();
g2d.setFont(font);
g2d.drawString(text, x, y);
}
}
public class WordListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
word = (String) wordComboBox.getSelectedItem();
updateWordPanel(word);
}
}
public class RotateListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
boolean leftSelected = leftBox.isSelected();
boolean rightSelected = rightBox.isSelected();
if (leftSelected && rightSelected) {
word = "OOPS";
updateWordPanel(word);
return;
}
if (!leftSelected && !rightSelected) {
return;
}
word = (String) wordComboBox.getSelectedItem();
updateWordPanel(word);
animation = new Animation(leftSelected);
new Thread(animation).start();
}
}
public class StopListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
if (animation != null) {
animation.setRunning(false);
animation = null;
}
}
}
public class Animation implements Runnable {
private boolean leftSelected;
private volatile boolean running;
public Animation(boolean leftSelected) {
this.leftSelected = leftSelected;
this.running = true;
}
#Override
public void run() {
while (running) {
updatePanel();
sleep(1000L);
if (leftSelected) {
word = rotateLeft(word);
} else {
word = rotateRight(word);
}
}
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
private String rotateLeft(String word) {
return word.substring(1) + word.substring(0, 1);
}
private String rotateRight(String word) {
return word.substring(word.length() - 1) +
word.substring(0, word.length() - 1);
}
private void updatePanel() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateWordPanel(word);
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
// Deliberately left blank
}
}
}
}
This is not a duplicate. All other solutions I tried were outdated.
So first look at this Image
I made that in eclipse today in Java.
It looks like a Visual Novel.
The point is I want to draw some text on the screen but don't know how to.
At first I only want to know:
How to draw text on screen and change it
Make something, such as an image or some text, clickable to move to
the next scene
Here is my current code:
package textboxes;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
public class test extends Applet implements Runnable, KeyListener {
private Image Image, Background;
private Image actor1, actor2;
private Image textbox;
private Graphics graphics;
private URL base;
private static testbg bg;
#Override
public void init(){
setSize(960, 540);
setBackground(Color.LIGHT_GRAY);
setFocusable(true);
Frame frame = (Frame)this.getParent().getParent();
frame.setTitle("School Scene");
try{
base = getDocumentBase();
}catch(Exception e){};
//getImages from disk
Background = getImage(base, "res/background.jpg");
actor1 = getImage(base, "res/actor1.jpg");
actor2 = getImage(base, "res/actor2.jpg");
textbox = getImage(base, "res/textbox.jpg");
}
public test(){
}
#Override
public void start(){
bg = new testbg();
Thread thread = new Thread(this);
thread.start();
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void run() {
bg.update();
repaint();
try{
Thread.sleep(17);
}catch(InterruptedException e){
e.printStackTrace();
}
}
#Override
public void update(Graphics g){
if(Image == null){
Image = createImage(this.getWidth(), this.getHeight());
graphics = Image.getGraphics();
}
graphics.setColor(getBackground());
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(getForeground());
paint(graphics);
g.drawImage(Image, 0, 0, this);
}
#Override
public void paint(Graphics g){
super.paint(g);
g.drawImage(Background, bg.getBgX(), bg.getBgY(), this);
g.drawImage(actor2, 40, 20, this);
g.drawImage(textbox, 80, 350, this);
}
public static testbg getBg() {
return bg;
}
}
This piece of code above is what I call test.java
if you are wondering about the Background part
the following piece of code is what I call testbg.java
package textboxes;
public class testbg {
private int bgX, bgY;
public testbg(){
bgX = 0;
bgY = 0;
}
public void update(){
}
public int getBgX(){
return bgX;
}
public int getBgY(){
return bgY;
}
public void setBgX(int bgX) {
this.bgX = bgX;
}
public void setBgY(int bgY) {
this.bgY = bgY;
}
}
Thanks for reading this much till the end...Now so can I know how to do it ??
As for the text, I have two solutions, but for what you want (and for what I know of CG games), I guess the first is the best.
This first solution is one that I found a long time ago for a problem of mine in StackOverflow (I don't remember where, sorry), in which includes de use of the several classes together to draw directly in the panel.
private final String message;
private final java.awt.geom.Rectangle2D.Float aboutMessageBounds;
private final AttributedString aboutMessageAttributedString;
private final AttributedCharacterIterator paragraph;
// The LineBreakMeasurer used to line-break the paragraph.
private java.awt.font.LineBreakMeasurer lineMeasurer;
// index of the first character in the paragraph.
private final int paragraphStart;
// index of the first character after the end of the paragraph.
private final int paragraphEnd;
#Override
public void init(){
(...)
java.util.Hashtable<TextAttribute, Object> textAtributMap =
new java.util.Hashtable<TextAttribute, Object>();
textAtributMap.put(TextAttribute.FAMILY, "Serif");
textAtributMap.put(TextAttribute.SIZE, new Float(26.0));
textAtributMap.put(TextAttribute.JUSTIFICATION, TextAttribute.JUSTIFICATION_FULL );
textAtributMap.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_DEMIBOLD );
textAtributMap.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON );
message = "This is a sample of a message.";
aboutMessageAttributedString = new AttributedString( aboutMessage, textAtributMap );
paragraph = aboutMessageAttributedString.getIterator();
paragraphStart = paragraph.getBeginIndex();
paragraphEnd = paragraph.getEndIndex();
(...)
}
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g ); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2 = (Graphics2D)g.create();
try {
g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
// Create a new LineBreakMeasurer from the paragraph.
// It will be cached and re-used.
if (lineMeasurer == null) {
FontRenderContext frc = g2.getFontRenderContext();
lineMeasurer = new java.awt.font.LineBreakMeasurer(paragraph, frc);
}
//You can scale it like I did. this part is not part of the code that I found.
g2.scale( ratio.scaleDx, ratio.scaleDy );
// Set break width to width of Component.
//these were the measures I used for a something in a game;
float breakWidth = 734.0f;
float drawPosY = 90.0f;
float posX0 = 30.0f;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
int next = lineMeasurer.nextOffset(breakWidth);
int limit = next;
if (limit <= message.length()) {
for (int i = lineMeasurer.getPosition(); i < next; ++i) {
char c = aboutMessage.charAt(i);
if (c == '\n') {
limit = i + 1;
break;
}
}
}
java.awt.font.TextLayout layout = lineMeasurer.nextLayout( breakWidth, limit, false );
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
// Note: this won't occur for the English text in this sample.
// Note: drawPosX is always where the LEFT of the text is placed.
float drawPosX = layout.isLeftToRight()
? posX0 : breakWidth - layout.getAdvance();
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
}
finally {
g2.dispose();
}
}
As for the second solution, you could use a JEditorPane or a JTextPane. See the Oracle tutorial for this mater:
https://docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html
I hop I have helped.
Have a nice day. :)
I'm trying to make a news ticker type thing where a string is entered and inside of a JPanel the text is looped.
I know it moves currently at about 90 pixels a second and that there is about 16 pixels per char in this font. So what I am asking is how I can use this information to make it so that after a timer runs for a certain time, a new animated text is spawned, and how after the 1st animated text leaves the screen completely, how to delete it from the memory.
This is what I got so far, the code is borrowed heavily from here : Java Animate JLabel, So also if you see any unneeded code in there, let me know.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
public static final int TRANSLATE_SCALE =2;
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int posX = 100;
private int posY = 50;
Timer t;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.TRUE);
}
t = new Timer(TIMER_DELAY, new TimerListener());
t.start();
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
actionMap.put(dir.Left + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
String s = "Hi, I'm trying to make a news ticker type thing where a string is entered and inside of a JPanel the text is looped.I know it moves currently at about 90 pixels a second and that there is about 16 pixels per char in this font. So what I am asking is how I can use this information to make it so that after a timer runs for a certain time, a new animated text is spawned, and how after the 1st animated text leaves the screen completely, how to delete it from the memory.";
g.drawString(s, posX, posY);
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
posX += dir.getX() * TRANSLATE_SCALE;
posY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
if(posX<-500)
{
t.stop();
}
};
}
enum Direction {
Left( KeyEvent.VK_LEFT, -1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setUndecorated(true);
frame.setSize(1600, 900);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.add(mainPanel);
mainPanel.setBounds(new Rectangle(1600,400));
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
EDIT:
I was also thinking that perhaps resetting the X position after a certain amount of time or distance, but the problem with that would be that it would require that the area would be blank for one screen length. If you have a work around for that, it would also be great.
You could check out the Marquee Panel. It uses a different approach than most scrollers. You add actual components to the panel and the components scroll.
This allows you to scroll text or images. You can use labels with HTML so you can have colored text etc. The message will continue to scroll until you manually stop the scrolling.
Currently there is no automatic mechanism for replacing a scrolling message. Although you could easily create a List or Queue of messages. Then when a message has finished scrolling completely you just remove the current message and add a new one.
Following is the section code from the MarqueePanel class you would need to change:
public void actionPerformed(ActionEvent ae)
{
scrollOffset = scrollOffset + scrollAmount;
int width = super.getPreferredSize().width;
if (scrollOffset > width)
{
scrollOffset = isWrap() ? wrapOffset + scrollAmount : - getSize().width;
// add code here to swap component from the List or Queue
}
repaint();
}
Of course you would also need to add a method to the class to add a component to the List or Queue.
Don't worry about the character width, as different fonts can produce variable character widths. Instead use FontMetrics to measure the String width and determine it the xPos <= -stringWidth, this is when the text would be fully off the left hand side of the screen.
You could use a Queue of some kind to manage text, popping of the next one as you need it. This example simply pops the last message onto the end of the queue, so it will keep repeating
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Queue<String> queue = new LinkedList<>();
queue.add("I have something to say, it's better to burn out then fade away");
queue.add("Banana peels");
queue.add("Don't worry if plan A fails, there are 25 more letters in the alphabet");
queue.add("When the past comes knocking, don't answer. It has nothing new to tell you");
queue.add("I know the voices in my head aren't real..... but sometimes their ideas are just absolutely awesome!");
TickerTapPane pane = new TickerTapPane();
pane.setMessages(queue);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TickerTapPane extends JPanel {
private Queue<String> queue;
private String message;
private int xPos;
public TickerTapPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (message == null) {
message = queue.remove();
xPos = getWidth();
}
xPos -= 4;
FontMetrics fm = getFontMetrics(getFont());
int stringWidth = fm.stringWidth(message);
if (xPos <= -stringWidth) {
queue.add(message);
xPos = getWidth();
message = queue.remove();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (message != null) {
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(message, xPos, yPos);
g2d.dispose();
}
}
protected void setMessages(Queue<String> queue) {
this.queue = queue;
}
}
}
I have written this code for custom JProgressBar in which "string painted" appears just after the progress line, but I want the same functionality in a vertical JProgressBar, please tell how can I do that???
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class R {
public static void main(String arg[]) throws Exception {
new R();
}
public R() throws Exception {
JFrame f = new JFrame();
CustomProgressBar b = new CustomProgressBar();
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setUndecorated(true);
f.setLayout(new FlowLayout());
b.setStringPainted(true);
f.add(b);
f.setVisible(true);
for(int i=0; i<101; i++) {
b.setValue(i);
Thread.sleep(50);
}
}
}
class CustomProgressBar extends JProgressBar{
private static final long serialVersionUID = 1L;
private boolean isStringToBePainted = false;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(isStringToBePainted ) {
Dimension size = CustomProgressBar.this.getSize();
int x = (int)( size.width * CustomProgressBar.this.getPercentComplete() );
int height = g.getFontMetrics().getHeight();
int d = g.getFontMetrics().getDescent();
int y = (size.height + height)/2-d;
String text = getString();
g.setColor(Color.BLACK );
g.drawString(text, x, 12);
}
}
#Override
public void setStringPainted(boolean b) {
isStringToBePainted=b;
}
}
You'd call getOrientation() and compare the results to SwingConstants.VERTICAL or SwingConstants.HORIZONTAL to determine the orientation of the JProgressBar.
For painting the text vertically, I found this page which gives two examples. The first one prints the characters "right side up". It does this by getting all the characters from the string and looping over them, painting each one and incrementing the y value. The second example prints the characters sideways, using AffineTransform to rotate the graphics context 90 degrees, then painting the string.
here's the relevant code from the form where JComponents should be drawn - this code creates and adds to the form a JComponent and also adds it to an ArrayList, as I need to be able to destroy the components later.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
public class frmMain extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
/**
* Creates new form frmMain
*/
public frmMain() {
initComponents();
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
// Pocitadlo tmp = new Pocitadlo(100, 40, 40, getFont());
// tmp.addActionListener(this);
// this.add(tmp);
// tmp.start();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int val, x, y;
val = 20;
x = (this.getWidth() / 2) - 10 + rnd.nextInt(20);
y = (this.getHeight() / 2) - 10 + rnd.nextInt(20);
try{
val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(this);
poc.add(tmp);
tmp.setVisible(true);
tmp.start();
this.add(tmp);
this.setTitle("Počet počítadiel: " + this.getComponentCount());
repaint();
tmp.repaint();
} catch(Exception e){
JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("counterFinished")){
Pocitadlo tmp = (Pocitadlo)e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
this.remove(tmp);
}
}
and here is the code of the JComponents i'm adding and naiively expect to be drawn:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class Pocitadlo extends JComponent implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f){
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
public void start(){
isRunning = true;
valueTimer.start();
}
public void stop(){
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("valueTimer")){
value--;
if(actionListener != null){
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if(value == 0 && actionListener != null){
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
}
if(value < maxValue / 2 && !blinkTimer.isRunning()) blinkTimer.start();
}
if(e.getActionCommand().equals("blinkTimer")){
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener){
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if(value > maxValue) value = maxValue;
repaint();
}
#Override
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillRect(getX(), getY(), getWidth(), getHeight());
if(isDisplayed){
g.setColor(Color.green);
g.fillRect(getX(), getY(), getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
g.drawString(value + "/" + maxValue, 0, 0);
}
//super.paintComponent(g);
}
}
this results in nothing - the JComponents are created and correctly added at least to the ArrayList, and its paintComponent method run, as well as the timer events, components are also correctly removed at least from the ArrayList when the countdown reaches 0, but doesn't draw anything and I have no idea why.
please note that the commented code in frmMain constructor kind of works, and if uncommented, it draws the JComponent... well... just the rectangles, doesn't draw any text, but I suppose that's another problem, however, if you're willing to help with that one too, I'd be glad.
also, if I'm using action events and listeners incorrectly, please ignore that, I'm a beginner at Java and would like to know how to do it correctly, but later, right now I just need to get this code to work so please focus on the issue, thank you.
yes, there's some dead things, like the font passed to the component, those are remains of my attempts to get the drawString to work.
You project is suffering from a number of really basic misconceptions about how painting in Swing works.
Because (at the time I tested it) you hadn't provided the full code, I had to make some assumptions.
Your expection is that the content pane of the JFrame is using a null layout. I'd find it difficult to see how you would mange this and have other components on the frame. In any case, I would isolate a separate container to act as the "main" view for your Pocitadlo. This allows you to continue using layout managers to manage the other components as well.
I'm a little concerned that you trap the entire Pocitadlo generation code in a try-catch simply to catch a numeric conversion. Since you've already applied a default value, it would be reasonable to trap the conversion in its own try-catch and ignore it if it fails (displaying a message as required). Now I'm coming in from the outside, so you may see a need for this, it just caught my eye...
Your valueTimer actionPerformed code is slightly erronous, as it's possible for the blinkTimer to continue running even after the valueTimer has completed. I corrected this by merging two of you if statements into a if-else statement instead...
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
I, personally, would use a JPanel over a JComponent. JPanel is opaque to start with.
The main problem is in you paintComponent method.
You should always call super.paintComponent, and on opaque components it should be called first.
You seem to think you have to correct for the components position when painting, for example g.fillRect(getX(), getY(), getWidth(), getHeight());. This is actually wrong. The graphics context has already been translated so that the x/y position of the component/graphics is now equal to 0x0. Have a read through Painting in AWT and Swing for a better explanation, but basically, this means, the top/left corner of you paint area is now 0x0.
You seem to think that text is painted from the top/left corner of the first character. This is actually wrong. Text is painted along it's base line. You need to compenstate for this by adjusting the y-position by the Fonts ascent value.
.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRepaint01 {
public static void main(String[] args) {
new TestRepaint01();
}
public TestRepaint01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainFrame extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
private JPanel body;
/**
* Creates new form frmMain
*/
public MainFrame() {
setLayout(new BorderLayout());
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
body = new JPanel(null);
add(body);
JButton btn = new JButton("Add");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int val, x, y;
val = 20;
x = (getWidth() / 2) - 10 + rnd.nextInt(20);
y = (getHeight() / 2) - 10 + rnd.nextInt(20);
// try {
// val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(MainFrame.this);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
poc.add(tmp);
tmp.start();
body.add(tmp);
body.repaint();
setTitle("Počet počítadiel: " + getComponentCount());
// } catch (Exception e) {
// JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
// }
}
});
add(btn, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("counterFinished")) {
System.out.println("Counter finished");
Pocitadlo tmp = (Pocitadlo) e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
body.remove(tmp);
// body.revalidate();
body.repaint();
}
}
}
public class Pocitadlo extends JPanel implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f) {
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
blinkTimer.stop();
}
public void start() {
isRunning = true;
valueTimer.start();
}
public void stop() {
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("valueTimer")) {
value--;
System.out.println("value = " + value);
if (actionListener != null) {
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
}
if (e.getActionCommand().equals("blinkTimer")) {
System.out.println("Blink");
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener) {
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if (value > maxValue) {
value = maxValue;
}
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
// location bad
g.fillRect(0, 0, getWidth(), getHeight());
if (isDisplayed) {
g.setColor(Color.green);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
FontMetrics fm = g.getFontMetrics();
g.drawString(value + "/" + maxValue, 0, fm.getAscent());
}
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
}
}
None of the problems you've encountered are super critical (sure, you program doesn't do what you want) and suggest that you've reached a pinnacle moment in your code abilities. I say this, because I made the exact same mistakes... ;)
Take a closer look at 2D Graphics and Custom Painting
I'd also take a look at Code Conventions for the Java Programming Language as you simply annoy people if you don't follow them ;)