Swing: faites Défiler vers le bas de JScrollPane, qui dépend de l'emplacement de la fenêtre actuelle
Je suis essayer de reproduire la fonctionnalité de Adium et la plupart des autres clients de chat que j'ai vu, dans lequel les barres de défilement avance vers le bas lorsque de nouveaux messages arrivent dans, mais seulement si vous êtes déjà là. En d'autres termes, si vous avez fait défiler quelques lignes, et sont en lecture, quand un nouveau message arrive, de ne pas sauter votre position au bas de l'écran; ce serait ennuyeux. Mais si vous faites défiler vers le bas, le programme, à juste titre, suppose que vous voulez voir les plus récents messages à tout moment, et donc, l'auto-rouleaux en conséquence.
J'ai eu un ours d'un temps à essayer d'imiter ce; la plate-forme semble lutter contre ce comportement à tous les coûts. Le mieux que je puisse faire est la suivante:
Dans le constructeur:
JTextArea chatArea = new JTextArea();
JScrollPane chatAreaScrollPane = new JScrollPane(chatArea);
//We will manually handle advancing chat window
DefaultCaret caret = (DefaultCaret) chatArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
Dans la méthode qui gère nouveau texte à venir dans:
boolean atBottom = isViewAtBottom();
//Append the text using styles etc to the chatArea
if (atBottom) {
scrollViewportToBottom();
}
public boolean isAtBottom() {
//Is the last line of text the last line of text visible?
Adjustable sb = chatAreaScrollPane.getVerticalScrollBar();
int val = sb.getValue();
int lowest = val + sb.getVisibleAmount();
int maxVal = sb.getMaximum();
boolean atBottom = maxVal == lowest;
return atBottom;
}
private void scrollToBottom() {
chatArea.setCaretPosition(chatArea.getDocument().getLength());
}
Maintenant, cela fonctionne, mais c'est janky et n'est pas idéal pour deux raisons.
- Par réglage de la position du curseur, quelle que soit la sélection, l'utilisateur peut avoir dans la zone de chat est effacé. Je peux imaginer que ce serait très irritant s'il est tentant de copier/coller.
- Depuis la promotion de la faire défiler le volet se produit après que le texte est inséré, il y a une fraction de seconde où la barre de défilement est dans la mauvaise position, et puis visuellement sauts vers la fin. Ce n'est pas l'idéal.
Avant de vous demander, oui j'ai lu cet article de blog sur Zone De Texte Défilant, mais le défaut de défilement vers le bas de comportement n'est pas ce que je veux.
Autres (mais à mon avis, pas tout à fait utile à cet égard) questions:
Réglage de la barre de défilement sur un jscrollpane
Faire un JScrollPane automatiquement défiler tout le chemin vers le bas.
Toute aide à ce sujet serait très apprécié.
Edit:
Comme par Devon_C_Miller les conseils, j'ai une meilleure façon de défilement vers le bas, la résolution de problème #1.
private void scrollToBottom() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
int endPosition = chatArea.getDocument().getLength();
Rectangle bottom = chatArea.modelToView(endPosition);
chatArea.scrollRectToVisible(bottom);
}
catch (BadLocationException e) {
System.err.println("Could not scroll to " + e);
}
}
});
}
J'ai encore le problème n ° 2.
- Vous pouvez le vérifier ma réponse là : stackoverflow.com/questions/4045722/...
Vous devez vous connecter pour publier un commentaire.
Prendre un coup d'oeil à la scrollRectToVisible(Rectangle r) et modelToView(int pos)
Qui devrait vous obtenir ce que vous cherchez sans perturber la sélection de l'utilisateur.
Comme pour la barre de défilement, essayer de forcer le rouleau et l'ajouter à se produire sur différents événements. Par exemple:
Basées sur des réponses précédentes et plus Google qui pratiquent j'ai fait un test dans lequel un Thread en permanence ajoute des Cordes à un JTextArea dans un JScrollPane. Je pense qu'il est important d'utiliser invokeLater ici, depuis le JScrollBar doit être mis à jour avec sa nouvelle valeur maximale avant nous faites défiler jusqu'à cette valeur. À l'aide de invokeLater, l'AWT Thread va prendre soin de cela.
Découvrez ce exemple.
Bien, je voudrais changer le code pour utiliser la suite qui est plus efficace:
J'ai passé les derniers jours à essayer différentes approches à ce problème. La meilleure solution que j'ai trouvé est de remplacer le gestionnaire de présentation de JScrollPane de la fenêtre d'affichage et de mettre en œuvre toutes les comportement de défilement là. Le jumpy barre de défilement bouton est parti, et c'est ultra-rapide -- si rapide qu'il a créé une autre condition de course, j'ai dû contourner. Les détails sont ici: http://www.java-forums.org/awt-swing/56808-scroll-bar-knob-jumpy-when-component-growing.html
Un peu de retard, mais ici, c'est quelque chose que j'ai trouvé: http://www.camick.com/java/source/SmartScroller.java
En essence, vous pouvez utiliser le code suivant:
Et ici le SmartScroller classe:
Vous pouvez trouver tous les détails ici: Comment faire JTextPane automatique uniquement lorsque la barre de défilement est en bas et le scroll lock est éteint?