2015-07-12 46 views
0

我的問題滾動位置很相似,這一個:JScrollPane的:後重新驗證

Set the vertical scroll to current position after revalidate

不同的是,我不想捲回至0,而是從更新前我救的位置子組件如下:

final int oldPos = scrollPanel.getVerticalScrollBar().getValue(); 
removeAll(); // Removes all components from the JPanel 
add(mList()); // Adds a bunch of content 
revalidate(); 
SwingUtilities.invokeLater(new Runnable() 
{ 
    @Override 
    public void run() 
    { 
     ml.getVerticalScrollBar().setValue(oldPos); 
    } 
}); 

此代碼獲取另一componenent的鼠標移動偵聽器觸發因此它被稱爲相當頻繁。

即使最初jscrollpane滾動到頂部有時oldPos不爲零,導致窗格向下滾動,儘管設置了該值。

即使我用力滾動到頂部,只要登錄oldPos價值,它並不總是0:

final int oldPos = scrollPanel.getVerticalScrollBar().getValue(); 
removeAll(); 
add(mList()); 
revalidate(); 
SwingUtilities.invokeLater(new Runnable() 
{ 
    @Override 
    public void run() 
    { 
     System.out.println(oldPos); // Sometimes this is > 2000 
     ml.getVerticalScrollBar().setValue(0); // scroll to the top 
    } 
}); 

我想如果oldPos = ...代碼先前執行的invokeLater的塊之前執行這種情況被調用。我怎樣才能解決這個問題?

更新:

我創建了一個SSCCE的建議通過@mKorbel:

更新2:我更新了SSCCE允許的JTextArea/JLabel的之間的切換,啓用/禁用的重新綁定監聽器,並在jTextArea和從jTextArea繼承的自定義類之間切換,覆蓋scrollRectToVisible方法。

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseMotionAdapter; 

import javax.swing.Box; 
import javax.swing.BoxLayout; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JSplitPane; 
import javax.swing.JTextArea; 
import javax.swing.ScrollPaneConstants; 
import javax.swing.SwingUtilities; 

public class Main { 
    static class MyTextArea extends JTextArea { 
    @Override 
    public void scrollRectToVisible(final Rectangle aRect) { 
     // supress scrollToRect in textarea 
    } 
    } 

    static final Box inner = Box.createVerticalBox(); 

    // if the jtextarea should contain text 
    private static boolean textEnabled = true; 
    private static boolean usejLabel = false; 
    private static boolean useRebinding = false; 
    private static boolean useLock = true; 
    private static boolean customTextarea = false; 

    private static boolean _locked = false; 

    public static void main(final String[] args) { 
    final JFrame frame = new JFrame(); 

    final JPanel insideScroll = insideScroll(); 
    final JScrollPane scrollpane = new JScrollPane(insideScroll); 
    scrollpane.setAutoscrolls(false); 
    scrollpane 
     .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 
    final JPanel rightPanel = new JPanel(); 
    rightPanel.setPreferredSize(new Dimension(300, 300)); 

    final MouseMotionAdapter listener = new MouseMotionAdapter() { 

     @Override 
     public void mouseDragged(final MouseEvent e) { 
     super.mouseDragged(e); 
     final boolean rebind = useRebinding; 

     final MouseMotionAdapter self = this; 
     if (rebind) { 
      rightPanel.removeMouseMotionListener(self); 
     } 
     final int pos = scrollpane.getVerticalScrollBar().getValue(); 
     if (!useLock || !_locked) { 
      if (useLock) { 
      _locked = true; 
      } 
      update(); 
      SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       if (rebind) { 
       rightPanel.addMouseMotionListener(self); 
       } 
       if (useLock) { 
       _locked = false; 
       } 
      } 
      }); 
     } 

     } 
    }; 
    rightPanel.addMouseMotionListener(listener); 

    // Add labels describing the problem 
    rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS)); 
    final JButton toggleButton = new JButton("Toggle text"); 
    final JButton toggleButtonLabel = new JButton("Toggle JLable/JTextArea"); 
    final JButton toggleButtonRebind = new JButton("Turn rebinding on"); 
    final JButton toggleButtonCustomTextArea = new JButton(
     "Toggle Custom Textarea"); 
    rightPanel.add(toggleButton); 
    rightPanel.add(toggleButtonLabel); 
    rightPanel.add(toggleButtonRebind); 
    rightPanel.add(toggleButtonCustomTextArea); 
    rightPanel 
     .add(new JLabel(
      "<html>If the text is disabled, you can press/drag<br> your mouse on on right side of the<br> window and the scrollbar will<br> stay in its position.")); 

    rightPanel 
     .add(new JLabel(
      "<html><br/>If the text is enabled, the scrollbar<br> will jump around when dragging<br> on the right side.")); 

    rightPanel 
     .add(new JLabel(
      "<html><br/>The problem does not occur when using JLabels instead of JTextArea")); 

    // enable/disable the text when the button is clicked. 
    toggleButton.addActionListener(new ActionListener() { 
     @Override 
     public void actionPerformed(final ActionEvent e) { 
     textEnabled = !textEnabled; 
     update(); 
     } 
    }); 

    toggleButtonLabel.addActionListener(new ActionListener() { 
     @Override 
     public void actionPerformed(final ActionEvent e) { 
     usejLabel = !usejLabel; 
     update(); 
     } 
    }); 
    toggleButtonRebind.addActionListener(new ActionListener() { 
     @Override 
     public void actionPerformed(final ActionEvent e) { 
     useRebinding = !useRebinding; 
     toggleButtonRebind.setText(useRebinding ? "Turn rebinding off" 
      : "Turn rebinding on"); 
     update(); 
     } 
    }); 
    toggleButtonCustomTextArea.addActionListener(new ActionListener() { 
     @Override 
     public void actionPerformed(final ActionEvent e) { 
     customTextarea = !customTextarea; 
     update(); 
     } 
    }); 

    final JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
     scrollpane, rightPanel); 

    frame.add(split); 

    // initialize the scrollpane content 
    update(); 

    frame.pack(); 
    frame.setVisible(true); 

    frame.setLocationRelativeTo(null); 

    } 

    // initializes the components inside the scrollpane 
    private static JPanel insideScroll() { 
    final JPanel panel = new JPanel(); 
    panel.setLayout(new BorderLayout()); 
    panel.add(inner, BorderLayout.NORTH); 

    return panel; 
    } 

    // replaces all components inside the scrollpane 
    private static void update() { 
    inner.removeAll(); 

    for (int i = 0; i < 30; i++) { 
     inner.add(buildRow(i)); 
    } 

    inner.revalidate(); 
    } 

    // build a single component to be inserted into the scrollpane 
    private static JPanel buildRow(final int i) { 
    final JPanel row = new JPanel(); 

    final Color bg = i % 2 == 0 ? Color.DARK_GRAY : Color.LIGHT_GRAY; 

    row.setBackground(bg); 
    row.setPreferredSize(new Dimension(300, 80)); 
    row.setLayout(new BorderLayout()); 

    row.add(textarea(bg), BorderLayout.CENTER); 

    return row; 
    } 

    // build the textarea to be inserted into the cells in the scroll pane 
    private static Component textarea(final Color bg) { 
    final String text = String.format("%d", (int) (1000 * Math.random())) 
     + " Lorem ipsum dolor si amet. Lorem ipsum dolor si amet. Lorem ipsum dolor si amet"; 
    if (usejLabel) { 
     final JLabel textarea = new JLabel(); 

     textarea.setBackground(bg); 
     if (textEnabled) { 
     textarea.setText(text); 
     } 

     return textarea; 
    } else { 
     final JTextArea textarea; 
     if (customTextarea) { 
     textarea = new MyTextArea(); 
     textarea.setDisabledTextColor(Color.cyan); 

     } else { 
     textarea = new JTextArea(); 
     textarea.setDisabledTextColor(Color.black); 

     } 

     textarea.setEnabled(false); 
     textarea.setLineWrap(true); 
     textarea.setBackground(bg); 
     textarea.setEditable(false); 
     if (textEnabled) { 
     textarea.setText(text); 
     } 

     return textarea; 
    } 

    } 
} 

通過這樣做,我意識到,只有當jscroll包含有自己的文字設置爲一個字符串的JTextArea元素出現問題。 在我真正的應用程序中,我需要自動換行的textareas,但即使禁用換行也會出現問題。

+0

我該如何解決這個問題? ==爲了更好地幫助發佈SSCCE/MVCE,短,可運行,可編譯 – mKorbel

+0

考慮使用['scrollRectToVisible'](http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent。 HTML#scrollRectToVisible(java.awt中。矩形))你想要滾動的組件 – MadProgrammer

+0

@MadProgrammer如何獲得矩形? –

回答

1

我找到了解決方案。整個問題不是JScrollPane本身,但如果文本不適合,JTextArea會自動調用scrollRectToVisible。這個調用通過視圖層次傳播到JScrollPane,導致它滾動到textarea。

在我的情況下,我有30 + JTextareas不斷變化的文本,這發生相當的offen。

我的解決方案是創建一個繼承自JTextArea的自定義類,並用空實現重寫scrollRectToVisible方法。

這裏是工作示例:

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseMotionAdapter; 

import javax.swing.Box; 
import javax.swing.BoxLayout; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JSplitPane; 
import javax.swing.JTextArea; 
import javax.swing.ScrollPaneConstants; 

public class Main { 
    // custom Textarea class 
    static class MyTextArea extends JTextArea { 
    @Override 
    public void scrollRectToVisible(final Rectangle aRect) { 
     // supress scrollToRect in textarea 
    } 
    } 

    static final Box inner = Box.createVerticalBox(); 

    public static void main(final String[] args) { 
    final JFrame frame = new JFrame(); 

    final JPanel insideScroll = insideScroll(); 
    final JScrollPane scrollpane = new JScrollPane(insideScroll); 
    scrollpane.setAutoscrolls(false); 
    scrollpane 
    .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 
    final JPanel rightPanel = new JPanel(); 
    rightPanel.setPreferredSize(new Dimension(300, 300)); 

    final MouseMotionAdapter listener = new MouseMotionAdapter() { 

     @Override 
     public void mouseDragged(final MouseEvent e) { 
     super.mouseDragged(e); 

     update(); 
     } 
    }; 
    rightPanel.addMouseMotionListener(listener); 

    // Add labels describing the problem 
    rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS)); 

    final JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 
     scrollpane, rightPanel); 

    frame.add(split); 

    // initialize the scrollpane content 
    update(); 

    frame.pack(); 
    frame.setVisible(true); 

    frame.setLocationRelativeTo(null); 

    } 

    // initializes the components inside the scrollpane 
    private static JPanel insideScroll() { 
    final JPanel panel = new JPanel(); 
    panel.setLayout(new BorderLayout()); 
    panel.add(inner, BorderLayout.NORTH); 

    return panel; 
    } 

    // replaces all components inside the scrollpane 
    private static void update() { 
    inner.removeAll(); 

    for (int i = 0; i < 30; i++) { 
     inner.add(buildRow(i)); 
    } 

    inner.revalidate(); 
    } 

    // build a single component to be inserted into the scrollpane 
    private static JPanel buildRow(final int i) { 
    final JPanel row = new JPanel(); 

    final Color bg = i % 2 == 0 ? Color.DARK_GRAY : Color.LIGHT_GRAY; 

    row.setBackground(bg); 
    row.setPreferredSize(new Dimension(300, 80)); 
    row.setLayout(new BorderLayout()); 

    row.add(textarea(bg), BorderLayout.CENTER); 

    return row; 
    } 

    // build the textarea to be inserted into the cells in the scroll pane 
    private static Component textarea(final Color bg) { 
    final String text = String.format("%d", (int) (1000 * Math.random())) 
     + " Lorem ipsum dolor si amet. Lorem ipsum dolor si amet. Lorem ipsum dolor si amet"; 

    final JTextArea textarea = new MyTextArea(); 
    textarea.setDisabledTextColor(Color.cyan); 

    textarea.setEnabled(false); 
    textarea.setLineWrap(true); 
    textarea.setBackground(bg); 
    textarea.setEditable(false); 
    textarea.setText(text); 

    return textarea; 
    } 

} 

更新:我只是碰到這種其他的線程來與禁用JTextArea中的自動滾動一個更好的解決方案 - 沒有子:

Java/Swing : JTextArea in a JScrollPane, how to prevent auto-scroll?