Как сделать прокрутку JScrollPane, чтобы следить за фокусом ввода? - PullRequest
10 голосов
/ 23 ноября 2011

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

Я пытался использовать KeyboardFocusManager для прослушивания изменений фокуса ввода, а затем вызывать scrollRectToVisible.

Вот SSCCE отображение моей текущей стратегии (просто скопируйте / вставьте и запустите!):

import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;

public class FollowFocus {

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

      public void run() {
        final int ROWS = 100;
        final JPanel content = new JPanel();
        content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
        content.add(new JLabel(
          "Thanks for helping out. Use tab to move around."));
        for (int i = 0; i < ROWS; i++) {
          JTextField field = new JTextField("" + i);
          field.setName("field#" + i);
          content.add(field);
        }

        KeyboardFocusManager.getCurrentKeyboardFocusManager()
                            .addPropertyChangeListener("focusOwner", 
                     new PropertyChangeListener() {

          @Override
          public void propertyChange(PropertyChangeEvent evt) {
            if (!(evt.getNewValue() instanceof JComponent)) {
              return;
            }
            JComponent focused = (JComponent) evt.getNewValue();
            if (content.isAncestorOf(focused)) {
              System.out.println("Scrolling to " + focused.getName());
              focused.scrollRectToVisible(focused.getBounds());
            }
          }
        });

        JFrame window = new JFrame("Follow focus");
        window.setContentPane(new JScrollPane(content));
        window.setSize(200, 200);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
      }
    });
  }
}

Если вы запустите этот пример, вы заметите, что он работает не очень хорошо.Он получает уведомления об изменении фокуса, но вызов scrollRectToVisible, похоже, не имеет никакого эффекта.В моем приложении (которое слишком сложно показать здесь) scrollRectToVisible работает примерно вдвое меньше, когда я вкладываю что-то за пределы области просмотра.

Существует ли установленный способ решения этой проблемы?Если это имеет какое-либо значение, приложение Swing построено на RCP Netbeans (и большинство наших клиентов используют Windows).

Ответы [ 4 ]

14 голосов
/ 23 ноября 2011

Мой комментарий к другому ответу:

scrollRectToVisible для самого компонента - вот и весь смысл этого метода ;-) Он передается по иерархии, пока не будет найден родитель, выполняющий прокрутку

... за исключением случаев, когда сам компонент обрабатывает его - как это делает JTextField: он реализован для горизонтальной прокрутки, чтобы сделать каретку видимой.Выход состоит в том, чтобы вызвать метод для родительского поля.

Edit

просто для ясности, замененная строка -

    content.scrollRectToVisible(focused.getBounds());
3 голосов
/ 23 ноября 2011

Вы должны взять Rectangle из JPanel и JViewPort, а затем сравнить, например,

Уведомление (против повторного голосования) для окончательного и приятного вывода потребовало некоторой работы для позиций в JViewPort

import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
///6048454/kak-sdelat-prokrutku-jscrollpane-chtoby-sledit-za-fokusom-vvoda
public class FollowFocus {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                final int ROWS = 100;
                final JPanel content = new JPanel();
                content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
                content.add(new JLabel(
                        "Thanks for helping out. Use tab to move around."));
                for (int i = 0; i < ROWS; i++) {
                    JTextField field = new JTextField("" + i);
                    field.setName("field#" + i);
                    content.add(field);
                }
                final JScrollPane scroll = new JScrollPane(content);
                KeyboardFocusManager.getCurrentKeyboardFocusManager().
                        addPropertyChangeListener("focusOwner", new PropertyChangeListener() {

                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        if (!(evt.getNewValue() instanceof JComponent)) {
                            return;
                        }
                        JViewport viewport = (JViewport) content.getParent();
                        JComponent focused = (JComponent) evt.getNewValue();
                        if (content.isAncestorOf(focused)) {
                            System.out.println("Scrolling to " + focused.getName());
                            Rectangle rect = focused.getBounds();
                            Rectangle r2 = viewport.getVisibleRect();
                            content.scrollRectToVisible(new Rectangle(rect.x, rect.y, (int) r2.getWidth(), (int) r2.getHeight()));
                        }
                    }
                });

                JFrame window = new JFrame("Follow focus");
                window.setContentPane(new JScrollPane(content));
                window.setSize(200, 200);
                window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                window.setVisible(true);
            }
        });
    }
}
0 голосов
/ 27 сентября 2013

Здесь jtextbox - это компонент, на который вы хотите сфокусироваться, а jscrollpane - это ваша полоса прокрутки:

jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x);
0 голосов
/ 23 ноября 2011

Одна из основных проблем в вашем коде:

focused.scrollRectToVisible(focused.getBounds());

Вы вызываете scrollRectToVisible для самого компонента! Предположительно опечатка. Сделайте вашу JScrollPane финальной переменной и вызовите

scrollPane.getViewport().scrollRectToVisible(focused.getBounds());
...