问题
I'm currently working on a console window in Swing. It's based on a JTextArea and works like a common command line. You type a command in one line and press enter. In the next line, the output is shown and under that output, you could write the next command.
Now I want, that you could only edit the current line with your command. All lines above (old commands and results) should be non editable. How can I do this?
回答1:
You do not need to create your own component.
This can be done (as in I have done it) using a custom DocumentFilter.
You can get the document from textPane.getDocument()
and set a filter on it by document.setFilter()
. Within the filter, you can check the prompt position, and only allow modifications if the position is after the prompt.
For example:
private class Filter extends DocumentFilter {
public void insertString(final FilterBypass fb, final int offset, final String string, final AttributeSet attr)
throws BadLocationException {
if (offset >= promptPosition) {
super.insertString(fb, offset, string, attr);
}
}
public void remove(final FilterBypass fb, final int offset, final int length) throws BadLocationException {
if (offset >= promptPosition) {
super.remove(fb, offset, length);
}
}
public void replace(final FilterBypass fb, final int offset, final int length, final String text, final AttributeSet attrs)
throws BadLocationException {
if (offset >= promptPosition) {
super.replace(fb, offset, length, text, attrs);
}
}
}
However, this prevents you from programmatically inserting content into the output (noneditable) section of the terminal. What you can do instead is either a passthrough flag on your filter that you set when you're about to add the output, or (what I did) set the document filter to null before appending the output, and then reset it when you're done.
回答2:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class OnlyEditCurrentLineTest {
public JComponent makeUI() {
JTextArea textArea = new JTextArea(8,0);
textArea.setText("> aaa\n> ");
((AbstractDocument)textArea.getDocument()).setDocumentFilter(
new NonEditableLineDocumentFilter());
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(textArea), BorderLayout.NORTH);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() { createAndShowGUI(); }
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new OnlyEditCurrentLineTest().makeUI());
f.setSize(320,240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class NonEditableLineDocumentFilter extends DocumentFilter {
@Override public void insertString(
DocumentFilter.FilterBypass fb, int offset, String string,
AttributeSet attr) throws BadLocationException {
if(string == null) {
return;
}else{
replace(fb, offset, 0, string, attr);
}
}
@Override public void remove(
DocumentFilter.FilterBypass fb, int offset,
int length) throws BadLocationException {
replace(fb, offset, length, "", null);
}
private static final String PROMPT = "> ";
@Override public void replace(
DocumentFilter.FilterBypass fb, int offset, int length,
String text, AttributeSet attrs) throws BadLocationException {
Document doc = fb.getDocument();
Element root = doc.getDefaultRootElement();
int count = root.getElementCount();
int index = root.getElementIndex(offset);
Element cur = root.getElement(index);
int promptPosition = cur.getStartOffset()+PROMPT.length();
//As Reverend Gonzo says:
if(index==count-1 && offset-promptPosition>=0) {
if(text.equals("\n")) {
String cmd = doc.getText(promptPosition, offset-promptPosition);
if(cmd.isEmpty()) {
text = "\n"+PROMPT;
}else{
text = "\n"+cmd+"\n xxxxxxxxxx\n" + PROMPT;
}
}
fb.replace(offset, length, text, attrs);
}
}
}
回答3:
AFAIK, you need to implement your own control
Maybe you could simulate it with a list of textfields(even enabled and odd disabled) or a mix of textfields/labels
EDIT:
I would bet for a non-editable textarea and an editable textfield. Type in textfield, press enter, add "command" and output to the textarea
回答4:
This is my implemitation of a document filter acting as a console in java. However with some modifications to allow me to have a " command area" and a "log area", meaning the results from commands print in the log area and the actual command prints in the command area. The log area is just another Jtext area that is nonEditable. I found thisthread to be helpful, so mabey someone trying to achive something similar to this implementation can find some pointers!
class NonEditableLineDocumentFilter extends DocumentFilter
{
private static final String PROMPT = "Command> ";
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset, String string,AttributeSet attr) throws BadLocationException
{
if(string == null)
{
return;
}
else
{
replace(fb, offset, 0, string, attr);
}
}
@Override
public void remove(DocumentFilter.FilterBypass fb, int offset,int length) throws BadLocationException
{
replace(fb, offset, length, "", null);
}
@Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length,String text, AttributeSet attrs) throws BadLocationException
{
Document doc = fb.getDocument();
Element root = doc.getDefaultRootElement();
int count = root.getElementCount();
int index = root.getElementIndex(offset);
Element cur = root.getElement(index);
int promptPosition = cur.getStartOffset()+PROMPT.length();
if(index==count-1 && offset-promptPosition>=0)
{
if(text.equals("\n"))
{
cmd = doc.getText(promptPosition, offset-promptPosition);
if(cmd.trim().isEmpty())
{
text = "\n"+PROMPT;
}
else
{
text = "\n" + PROMPT;
}
}
fb.replace(offset, length, text, attrs);
}
}
}
回答5:
What about that, when ">> " is the beginning of every line in the command line where the user can input a command:
textArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
int code = event.getKeyCode();
int caret = textArea.getCaretPosition();
int last = textArea.getText().lastIndexOf(">> ") + 3;
if(caret <= last) {
if(code == KeyEvent.VK_BACK_SPACE) {
textArea.append(" ");
textArea.setCaretPosition(last + 1);
}
textArea.setCaretPosition(textArea.getText().length());
}
}
});
来源:https://stackoverflow.com/questions/10030477/make-parts-of-a-jtextarea-non-editable-not-the-whole-jtextarea