Jak określić numer pierwszej widocznej linii i liczby linii widocznych w przewijalnej JTextArea (JTextArea wewnątrz JScrollPane)?Jak określić, które linie są widoczne w przewijanej JTextArea?
Odpowiedz
Ok, to jest moje zdanie na ten problem ... (Nicea pytanie chociaż)
Jest mały rozpatrzenie trzeba mieć z tego rozwiązania. Zwróci częściowo wyświetlane linie.
public class TestTextArea {
public static void main(String[] args) {
new TestTextArea();
}
public TestTextArea() {
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) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestTextAreaPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestTextAreaPane extends JPanel {
private JTextArea textArea;
private JTextArea viewText;
public TestTextAreaPane() {
setLayout(new GridLayout(2, 1));
textArea = new JTextArea(20, 100);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
textArea.setText(loadText());
viewText = new JTextArea(20, 100);
viewText.setWrapStyleWord(false);
viewText.setLineWrap(false);
viewText.setEditable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
add(scrollPane);
add(viewText);
scrollPane.getViewport().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (textArea.getText().length() > 0) {
JViewport viewport = (JViewport) e.getSource();
Rectangle viewRect = viewport.getViewRect();
Point p = viewRect.getLocation();
int startIndex = textArea.viewToModel(p);
p.x += viewRect.width;
p.y += viewRect.height;
int endIndex = textArea.viewToModel(p);
if (endIndex - startIndex >= 0) {
try {
viewText.setText(textArea.getText(startIndex, (endIndex - startIndex)));
} catch (BadLocationException ex) {
ex.printStackTrace();
viewText.setText(ex.getMessage());
}
}
}
}
});
}
protected String loadText() {
String text = null;
File file = new File("src/testtextarea/TestTextArea.java");
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
StringBuilder sb = new StringBuilder(128);
String read = null;
while ((read = br.readLine()) != null) {
sb.append(read).append("\n");
}
text = sb.toString();
} catch (IOException exp) {
exp.printStackTrace();
} finally {
try {
br.close();
} catch (Exception e) {
}
}
return text;
}
}
}
Ciekawe pytanie, które mi zajęło, ale myślę, że mam dość ważny odpowiedź. Jednak mogą istnieć lepsze sposoby; zachęcamy do komentowania, aby poprawić odpowiedź.
Stategy:
- Znajdź których rzędy są widoczne przy użyciu FontMetrics i getVisibleRect()
- znaleźć zawartość widzialnego rzędach.
Tak więc, moim pomysłem jest, że powinniśmy zacząć od widocznego recta. Na tej podstawie możemy dowiedzieć się, co jest pierwszym widocznym pionowym przesunięciem (getVisibleRect().y
) i końcem widocznego przesunięcia pionowego (getVisibleRect().y+getVisibleRect().height
). Kiedy już to zrobimy, za pomocą wysokości czcionki możemy ustalić, które rzędy są widoczne.
Druga część jest dowiedzieć się, co robi te wiersze zawierają. To tutaj używam Utilities
z getRowStart()
i getRowEnd()
wchodzi w grę.
Oto przykładowy kod z czego się detalami (wynik są wyprowadzane do konsoli, jak zmienić rozmiar ramki lub przewijania textarea):
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Utilities;
public class TestTextAre {
private void initUI() {
JFrame frame = new JFrame(TestTextAre.class.getSimpleName());
final JTextArea ta = new JTextArea(5, 25);
ta.setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has "
+ "been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of "
+ "type and scrambled it to make a type specimen book.\n It has survived not only five centuries, but also the "
+ "leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the"
+ " release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing "
+ "software like Aldus PageMaker including versions of Lorem Ipsum.");
ta.setColumns(20);
ta.setEditable(false);
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
JScrollPane scrollpane = new JScrollPane(ta);
scrollpane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
printTAVisibleInfo(ta);
}
});
frame.add(scrollpane);
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
printTAVisibleInfo(ta);
}
});
frame.pack();
frame.setVisible(true);
}
private void printTAVisibleInfo(final JTextArea ta) {
List<Row> taInfo = getTAInfo(ta);
int y1 = ta.getVisibleRect().y;
int y2 = y1 + ta.getVisibleRect().height;
int lineHeight = ta.getFontMetrics(ta.getFont()).getHeight();
int startRow = (int) Math.ceil((double) y1/lineHeight);
int endRow = (int) Math.floor((double) y2/lineHeight);
endRow = Math.min(endRow, taInfo.size());
System.err.println(startRow + " " + endRow);
for (int i = startRow; i < endRow; i++) {
System.err.println(taInfo.get(i) + " is visible");
}
}
private List<Row> getTAInfo(final JTextArea ta) {
List<Row> taInfo = new ArrayList<TestTextAre.Row>();
int start = 0;
int end = -1;
int i = 0;
try {
do {
start = Utilities.getRowStart(ta, end + 1);
end = Utilities.getRowEnd(ta, start);
taInfo.add(new Row(i, start, end, ta.getDocument().getText(start, end - start)));
i++;
} while (end < ta.getDocument().getLength());
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return taInfo;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestTextAre().initUI();
}
});
}
public static class Row {
private final int row;
private final int start;
private final int end;
private final String text;
public Row(int row, int start, int end, String text) {
super();
this.row = row;
this.start = start;
this.end = end;
this.text = text;
}
public int getRow() {
return row;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
public String getText() {
return text;
}
@Override
public String toString() {
return "Row " + row + " contains " + text + " (" + start + "," + end + ")";
}
}
}
Jeśli masz również poziomy pasek przewijania, myślę powinieneś być w stanie obliczyć poziome przesunięcia z FontMetrics.stringWidth()
.
+1 ale łatwiej jest użyć metody viewToModel() uboczny widoczny prostokąt y zamiast obliczania z parametrów czcionki – StanislavL
@StanislavL Tak masz rację. Rozwiązanie MadProgrammer jest bardziej eleganckie. –
+1 dla ładniejszego rozwiązania –
Wygląda na to, że viewToModel() jest drogą do zrobienia. Dzięki. – Ma99uS