Остановить JScrollPane от большого количества перерисовок при прокрутке

#java #performance #swing #jscrollpane

#java #Производительность #качать #jscrollpane

Вопрос:

У меня длинный текст в текстовой области, помещенный на панель прокрутки. Я заметил, что когда я поворачиваю колесо мыши, текст сильно перерисовывается, может быть, даже полностью.

Это демонстрационное приложение:

 import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class TextArea {
  public static void main(String[] args) {
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    final JTextArea textArea = new JTextArea();
    textArea.setColumns(80);
    textArea.setRows(20);
    textArea.setLineWrap(true);
    final JScrollPane scrollPane = new JScrollPane(textArea);
    textArea.append("1234567890 ".repeat(20000));  // Java 11
    frame.add(scrollPane);
    frame.pack();
    frame.setVisible(true);
  }
}
  

Перерисовка может быть легко обнаружена, если вы поместите точку останова ведения журнала в sun.java2d.SunGraphics2D.drawString(String, float, float) метод. Для меня он регистрирует пару тысяч обращений, когда я поворачиваю колесо мыши на одну единицу (количество обращений зависит от размера кадра). Если я остановлюсь в точке останова, я увижу, что y положение строки для рисования сильно отличается, и разница намного больше, чем высота моего экрана.

Я думаю, что лучше перерисовать только видимые строки или, может быть, даже недавно появившиеся строки.

Можно JScrollPane ли как-то настроить, чтобы оптимизировать количество вызовов рисования? Или это неоптимизированная вещь в Swing?

Комментарии:

1. «он регистрирует пару тысяч обращений, когда я поворачиваю колесо мыши на одну единицу » Сколько он регистрирует для одной перерисовки? У вас там довольно большой кусок текста, не уверен, сколько строк получится. Проверяет ли он границы строк внутри этого метода? Таким образом, он попытается нарисовать все строки для одной перерисовки.

2. @matt, я проверю frame.repaint() завтра и опубликую результат.

3. Это все еще не имеет смысла. Ваш JScrollPane приводит к тому, что ваша JTextArea перерисовывается, возможно, один раз, максимум два или три. Вы выбрали метод, которому нужно следовать. «drawString», который, конечно, часто вызывается, потому что в вашей текстовой области много строк. Frame.repaint не имеет к этому никакого отношения. Возможно, вам следует начать с изучения руководств по рисованию java2d и изучения того, как работает swing painting.

4. @Мэтт, я не понимаю, что я должен проверить тогда. Итак, я проверил, что я намеревался. Когда я просто открываю приложение, происходит около 5 тыс. вызовов drawString. Когда я прокручиваю вниз на одну единицу прокрутки, их становится еще около 1700. Затем я прокрутил 10 раз и получил около 17000 вызовов. При отладке я вижу, что каждый вызов содержит довольно длинную строку для рендеринга, это безумие: почему их слишком много? Не могли бы вы уточнить, что вы имеете в виду под «изучением того, как работает swing painting»? Вы имеете в виду, что Swing действительно не является оптимальным, и я ничего не могу здесь сделать?

5. Есть ли проблема с производительностью? Из опубликованного кода у вас есть длинная строка текста, которую нужно перенести. Итак, я бы предположил, что логика выполняет перенос каждый раз? Что произойдет, если вы используете: textArea.append("1234567890 n".repeat(20000)); чтобы каждая строка текста находилась на новой строке?

Ответ №1:

Когда я запускаю этот код и вращаю колесо прокрутки мыши, графический интерфейс не мерцает и не меняется вообще. Единственная причина, по которой я вижу, что мое колесо прокрутки что-то делает JScrollPane , — это перемещение вкладки вверх.

Я скомпилирован в соответствии со стандартом Java 8, используя Java 14.0.2. Мой компьютер работает на Windows 10.

Неудивительно, поскольку текст идентичен.

Попробуйте этот полный исполняемый код и посмотрите, имеет ли это значение.

 import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class LargeTextArea implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new LargeTextArea());
    }

    @Override
    public void run() {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        final JTextArea textArea = new JTextArea();
        textArea.setColumns(80);
        textArea.setRows(20);
        textArea.setLineWrap(true);
        textArea.append("1234567890 ".repeat(20000)); // Java 11
        
        final JScrollPane scrollPane = new JScrollPane(textArea);
        frame.add(scrollPane);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
}
  

Комментарии:

1. Под перерисовкой я подразумеваю не мерцание, а использование вычислений…

Ответ №2:

JTextArea действительно, генерирует слишком много вызовов рисования для длинной строки, кажется, что она полностью перерисовывает ее, и из-за мягкой упаковки она разделяется.

Однако, углубляясь, видно, что во время прокрутки область отсечения устанавливается довольно ограничительно (для вновь видимого пространства), поэтому Graphics2D может пропустить рисование всех строк, которые находятся за пределами отсечения.

Так что не нужно беспокоиться, потому что на самом деле не нужно много рисовать.