public class Frame2 extends JFrame {
private JPanel contentPane;
Frame1 frms = new Frame1();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Frame2 frame = new Frame2();
frame.setVisible(true);
frame.setResizable(false);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Frame2() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(250, 100, 799, 526);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
// ...
final JTextPanePlus pnText1 = new JTextPanePlus(); // final because listener requires this
pnText1.setImage(icon4, -70, -100, 600, 500);
pnText1.setVisible(false);
pnText1.setFont(new Font("Tahoma", Font.PLAIN , 16));
pnText1.setEditable(false);
pnText1.setForeground(Color.WHITE);
pnText1.setText("Some text dasdasdasdasdasdas");
pnText1.setBounds(60, 343, 527, 111);
contentPane.add(pnText1);
//...
backgroundLabel.addMouseListener(new MouseAdapter(){
@Override
public void mouseClicked(MouseEvent arg0) {
pnText1.setVisible(true);
}
});
// ...
}
Method that picks an image for JTextPane
public class JTextPanePlus extends JTextPane {
Image image;
int x,y,width,height;
public JTextPanePlus(){
super();
}
public JTextPanePlus(String text){
super();
}
public void setImage(ImageIcon icon, int x, int y, int width, int height) {
this.image = icon.getImage();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
setOpaque(false);
repaint();
}
public void paint(Graphics g){
g.drawImage(image, x, y, width, height, null);
super.paint(g);
}
}
I'm having trouble making my text of JTextPane (JTextPanePlus) to appear slightly delayed between each character. Basically I want to create an effect like for a novel or adventure. And if user doesn't want to wait for whole text to appear, I could add a "mouseListener" and make the text appear at once (stop the method).
Now the problem is that I got no idea how to do this. Most topics around here covered things for JTextArea's and I'm not sure if the same can be done for JTextPane.
I tried to do "for(char c : s.toCharArray())" but then I have no idea what to do with "c" and this method seems to only work for print. Tried to use "Thread.sleep" but this freezes the whole JFrame. Java.swing.timer could work but then JTextPane doesn't support ".append" when used.
I'm running out of options and I'm not an expert for this. This is my first time working with something this hard. Also I'm truly sorry if this wasn't explained at best.
CodePudding user response:
The basic idea is the same for JTextPane
and JTextArea
. The problem is, JTextPane
is WAY more powerful in terms of it's rendering capabilities and doesn't provide some of the helper methods that JTextArea
does, like append
, instead, you'll have to do that yourself.
Having said that, this is a VERY basic example. This example loads the "Star Wars, A New Hope" script from an embedded file resource and prints it, like a type writer.
If you tap Stop, it will stop the Timer
and append the remaining text for the current line. Funny enough, if you start it again, it will pick up where it left off, neat side effect.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.Timer;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextPane textPane;
private Timer timer;
private List<String> lines;
private String currentLine;
private int currentLinePosition;
public TestPane() {
setLayout(new BorderLayout());
textPane = new JTextPane();
add(new JScrollPane(textPane));
JPanel actionsPane = new JPanel(new GridBagLayout());
JButton makeItSo = new JButton("Make it so");
JButton stop = new JButton("Stop");
actionsPane.add(makeItSo);
makeItSo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
makeItSo();
makeItSo.setEnabled(false);
stop.setEnabled(true);
}
});
actionsPane.add(stop);
stop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stopTimer();
makeItSo.setEnabled(true);
stop.setEnabled(false);
}
});
add(actionsPane, BorderLayout.SOUTH);
loadScript();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void loadScript() {
lines = new ArrayList<>(128);
try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/scripts/StarWarANewHope.txt")))) {
for (String line = br.readLine(); line != null; line = br.readLine()) {
lines.add(line);
}
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
protected int startPosition(String text) {
if (text.isEmpty() || text.isBlank()) {
return 0;
}
int index = 0;
while (Character.isWhitespace(text.charAt(index))) {
index ;
}
return index;
}
protected void insertWithOutError(String text) {
try {
insert(text);
} catch (BadLocationException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
protected void insert(String text) throws BadLocationException {
Document document = textPane.getDocument();
document.insertString(document.getLength(), text, null);
}
protected void stopTimer() {
if (timer != null) {
timer.stop();
timer = null;
}
if (currentLine != null) {
if (currentLinePosition < currentLine.length()) {
String text = currentLine.substring(currentLinePosition);
try {
insert(text);
insertWithOutError("\n");
} catch (BadLocationException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
currentLine = null;
}
protected void makeItSo() {
stopTimer();
if (lines.isEmpty()) {
return;
}
try {
// Pop the first line
currentLine = lines.remove(0);
int offset = startPosition(currentLine);
if (currentLine.isBlank() || offset == currentLine.length()) {
insertWithOutError("\n");
} else {
String leading = currentLine.substring(0, offset);
insert(leading);
}
currentLinePosition = 0;
currentLine = currentLine.trim();
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (currentLinePosition < currentLine.length()) {
try {
String next = currentLine.substring(currentLinePosition, currentLinePosition 1);
insert(next);
currentLinePosition ;
} catch (BadLocationException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
stopTimer();
}
} else {
insertWithOutError("\n");
makeItSo();
}
}
});
timer.start();
} catch (BadLocationException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Now, JTextPane
can support StyledDocument
s, this makes it some what, complicated work with. You will note that I've not even tried to support "attributes", you'll have to figure that mess out for yourself