Results 1 to 7 of 7
Thread: forcing scrollpane to autoscroll
- 05-19-2012, 10:51 PM #1
Member
- Join Date
- Oct 2011
- Posts
- 41
- Rep Power
- 0
forcing scrollpane to autoscroll
Ok, this one should be easy, but I cant quite figure it out. Ive done some research, and found the way your supposed to do it, but it doesnt work. What I want to do is, in my drawing panel, when you try to draw too far to the right, resize the panel, and have it scroll all the way to the right. Here is my code:
Java Code:public void scrollRight() { getHorizontalScrollBar().setValue(getHorizontalScrollBar().getMaximum()); getHorizontalScrollBar().updateUI(); }
Last edited by yemista; 05-19-2012 at 10:55 PM.
- 05-20-2012, 07:07 AM #2
Re: forcing scrollpane to autoscroll
1. Don't call updateUI(). That's not what the method is for.
2. How did you 'resize' the panel? There's (at least) one correct way and several wrong ways to try that.
3. After 'resizing' the panel, did you revalidate() it's container / hierarchy?
To get better help sooner, post a SSCCE (Short, Self Contained, Correct (Compilable), Example) that demonstrates the problem.
dbIf you're forever cleaning cobwebs, it's time to get rid of the spiders.
- 05-20-2012, 06:41 PM #3
Member
- Join Date
- Oct 2011
- Posts
- 41
- Rep Power
- 0
Re: forcing scrollpane to autoscroll
ok, so i stripped away much of my old code, and created a SSCCE. The main difference between this and my code is that this doesnt have all the functions for drawing, but instead increases the width of the canvas on a mouseClicked event, but after that the behavior should be the same, resize the canvas, and supposedly, scroll to the right.
Java Code:/** * To change this template, choose Tools | Templates * and open the template in the editor. */ package test; import javax.swing.*; public class Test { /** * @param args the command line arguments */ public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new MyFrame(); } }); } } /** * To change this template, choose Tools | Templates * and open the template in the editor. */ package test; import java.awt.*; import javax.swing.*; import java.util.*; import java.awt.event.*; public class DrawingPanel extends JScrollPane implements ComponentListener/**, MouseMotionListener */{ private double width_percent; private CanvasPanel canvaspanel; private int padding; public DrawingPanel(Component content) { super(content); addComponentListener(this); canvaspanel = (CanvasPanel)content; setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); //addMouseMotionListener(this); } @Override public Dimension getPreferredSize() { return new Dimension((int)(this.getParent().getWidth()), this.getParent().getHeight()); } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { setPreferredSize(new Dimension((int)(getParent().getWidth()), getParent().getHeight())); canvaspanel.componentResized(e); revalidate(); } public void componentShown(ComponentEvent e) { } /** public void mouseMoved(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )); canvaspanel.mouseMoved(e); //System.out.println(getHorizontalScrollBar().getValue()); } public void mouseDragged(MouseEvent e) { }*/ public void scrollDown() { getVerticalScrollBar().setValue(getVerticalScrollBar().getMaximum()); getVerticalScrollBar().updateUI(); } public void scrollRight() { //System.out.println("here"); this.getHorizontalScrollBar().setValue(this.getHorizontalScrollBar().getMaximum()); this.getHorizontalScrollBar().updateUI(); } } /** * To change this template, choose Tools | Templates * and open the template in the editor. */ package test; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; public class CanvasPanel extends JComponent implements ComponentListener, MouseMotionListener, MouseListener { private Image image; private Graphics2D graphics2D; private int height; private int width; private int old_height; private int old_width; private int old_x, old_y; private DrawingPanel father; public CanvasPanel(int width, int height) { super(); this.width = width; this.height = height; old_width = width; old_height = height; setPreferredSize(new Dimension(width, height)); //this.setSize(width, height); revalidate(); //this.setVisible(true); addMouseMotionListener(this); addMouseListener(this); } public void setFather(DrawingPanel p) { father = p; } @Override public void paintComponent(Graphics g){ super.paintComponent(g); if(image == null){ image = createImage(getParent().getSize().width, getParent().getSize().height); graphics2D = (Graphics2D)image.getGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else if(this.width < getParent().getSize().width || this.height < getParent().getSize().height) { image = createImage(getParent().getSize().width, getParent().getSize().height); this.width = getParent().getSize().width; this.height = getParent().getSize().height; graphics2D = (Graphics2D)image.getGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else if(old_height < height || old_width < width) { //System.out.println("here"); setSize(width, height); image = createImage(width, height); graphics2D = (Graphics2D)image.getGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if(old_width < width) father.scrollRight(); if(old_height < height) father.scrollDown(); old_width = width; old_height = height; getParent().validate(); } clear(); g.drawImage(image, 0, 0, null); } @Override public void paint(Graphics g){ super.paintComponent(g); paintComponent(g); } @Override public Dimension getPreferredSize() { return new Dimension(width, height); } public void clear(){ graphics2D.setPaint(Color.white); graphics2D.fillRect(0, 0, this.width, this.height); graphics2D.setPaint(Color.black); } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { if(getParent().getWidth() > width || getParent().getHeight() > height) { this.width = getParent().getWidth(); this.height = getParent().getHeight(); } repaint(); revalidate(); } public void componentShown(ComponentEvent e) { } public void mouseMoved(MouseEvent e) { } public void mouseDragged(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseClicked(MouseEvent e) { width*= 2; SwingUtilities.invokeLater(new Runnable() { public void run() { repaint(); } }); } } /** * To change this template, choose Tools | Templates * and open the template in the editor. */ package test; import java.awt.*; import javax.swing.*; import java.util.*; import java.awt.event.*; public class MyFrame extends JFrame implements ComponentListener { private int width; private int height; private Container content; private CanvasPanel canvas; private DrawingPanel drawingpanel; public MyFrame() { super(); Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension screen = toolkit.getScreenSize(); width = (screen.width > 800 ? 800 : screen.width); height = (screen.height > 600 ? 600 : screen.width); this.setSize(width,height); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); content = this.getContentPane(); content.setSize(width,height); canvas = new CanvasPanel(width, height); drawingpanel = new DrawingPanel(canvas); canvas.setFather(drawingpanel); //mainpanel.addComponentListener(this); content.add(drawingpanel); pack(); this.setVisible(true); this.addComponentListener(this); } public void componentResized(ComponentEvent e) { if(this.getWidth() < this.width){ setSize(this.width,this.getHeight()); } if(this.getHeight() < this.height) { setSize(getWidth(),this.height); } drawingpanel.componentResized(e); content.validate(); //pack(); } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } }
Last edited by DarrylBurke; 05-20-2012 at 07:30 PM. Reason: Edited to overcome forum formatting bug
- 05-20-2012, 07:48 PM #4
Re: forcing scrollpane to autoscroll
4 classes complete with inane IDE generated comments is not a SSCCE. But looking through that mess, I do see state-changing method calls within a painting method override. You need to get rid of that immediately.
Here's a working SSCCE. Figure out how to adapt it to your program.Java Code:import java.awt.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; public class ScrollOnResize { public static void main(String[] args) throws Exception { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new ScrollOnResize().makeUI(); } }); } public void makeUI() { final JPanel panel = new JPanel(); panel.setBorder(new CompoundBorder(new LineBorder(Color.RED), new LineBorder(Color.BLUE))); final JScrollPane scrollPane = new JScrollPane(panel); final JSlider slider = new JSlider(400, 4000, 400); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (!slider.getValueIsAdjusting()) { int newSize = slider.getValue(); panel.setPreferredSize(new Dimension(newSize, newSize)); panel.revalidate(); final JScrollBar scrollBar = scrollPane.getHorizontalScrollBar(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { scrollBar.setValue(scrollBar.getMaximum()); } }); } } }); JFrame frame = new JFrame(); frame.add(scrollPane, BorderLayout.CENTER); frame.add(slider, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setLocationRelativeTo(null); frame.setVisible(true); } }
If you're forever cleaning cobwebs, it's time to get rid of the spiders.
- 05-26-2012, 08:13 AM #5
Re: forcing scrollpane to autoscroll
I'm not sure if my code would be adaptable to your needs, but this seems to be a common problem, and I think my solution is the cleanest one around.
I created a custom text component and I wanted it to behave like a typical terminal. If it's scrolled to the bottom when a new line is appended, it remains scrolled to the bottom. If it's not scrolled to the bottom when new text is added, it scrolls its viewport so the same text remains visible until it scrolls off the top of the buffer.
I achieved this by customizing the viewport layout manager of JScrollPane and making the component that was to be placed in the JScrollPane implement a new interface, StickyScrollable. StickyScrollable's only method (in addition to those of Scrollable, which it extends) informs the scroll pane where the component wants the viewport scrolled.
Java Code:package krum.swing; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import javax.swing.JViewport; import javax.swing.ViewportLayout; public class StickyViewportLayout extends ViewportLayout { private static final long serialVersionUID = 1L; protected int previousHeight = 0; // TODO: stick all Scrollables to the bottom, and honor // the desired scroll offset of StickyScrollables. @Override public void layoutContainer(Container parent) { JViewport viewport = (JViewport) parent; Component view = viewport.getView(); if(view instanceof StickyScrollable) { StickyScrollable component = (StickyScrollable) view; Rectangle visible = viewport.getViewRect(); Dimension size = viewport.getViewSize(); //boolean atBottom = (visible.y == size.height - visible.height); boolean atBottom = (visible.y == size.height - visible.height || (previousHeight < visible.height && size.height > visible.height)); previousHeight = size.height; super.layoutContainer(parent); if(atBottom) { // lie to the component, make it think we're scrolling it where it wants component.getScrollOffset(); // actually scroll it to the bottom size = viewport.getViewSize(); visible.y = size.height - visible.height; viewport.setViewPosition(visible.getLocation()); } else { Point scrollOffset = component.getScrollOffset(); visible.x += scrollOffset.x; if(visible.x < 0) visible.x = 0; visible.y += scrollOffset.y; if(visible.y < 0) visible.y = 0; viewport.setViewPosition(visible.getLocation()); } } else super.layoutContainer(parent); } }
Java Code:package krum.swing; import java.awt.Point; import javax.swing.Scrollable; public interface StickyScrollable extends Scrollable { /** * Called by a <tt>StickyScrollPane</tt>'s viewport layout to determine * where a component wants its viewport scrolled relative to the current * viewport. This method should have the side effect of resetting the * component's desired scroll offset to (0, 0). * * @return this component's desired relative scroll * @see StickyScrollPane */ public abstract Point getScrollOffset(); }
Java Code:package krum.swing; import java.awt.Component; import javax.swing.JScrollPane; /** * A scroll pane with a viewport layout that recognizes * <tt>StickyScrollable</tt> components. If the viewport is at the bottom * when the component's height increases, it will be kept at the bottom. If * the component wishes to adjust its viewport, such as to maintain a view of * the same data as it scrolls through a circular buffer, it can communicate * this to the layout manager using the methods of <tt>StickyScrollable</tt>. * * @author Kevin Krumwiede (kjkrum@gmail.com) * @see StickyScrollable */ public class StickyScrollPane extends JScrollPane { private static final long serialVersionUID = 1L; public StickyScrollPane() { super(); replaceViewportLayout(); } public StickyScrollPane(Component view, int vsbPolicy, int hsbPolicy) { super(view, vsbPolicy, hsbPolicy); replaceViewportLayout(); } public StickyScrollPane(Component view) { super(view); replaceViewportLayout(); } public StickyScrollPane(int vsbPolicy, int hsbPolicy) { super(vsbPolicy, hsbPolicy); replaceViewportLayout(); } protected void replaceViewportLayout() { viewport.setLayout(new StickyViewportLayout()); } }
Last edited by kjkrum; 05-26-2012 at 08:16 AM.
Get in the habit of using standard Java naming conventions!
- 05-26-2012, 09:10 AM #6
Re: forcing scrollpane to autoscroll
If I'm reading that description correctly, you can achieve it by setting the caret's update policy.
This code was originally posted at https://forums.oracle.com/forums/thr...readID=1352982 with a link to camickr's Text Area Scrolling « Java Tips Weblog
Java Code:import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import javax.swing.*; import javax.swing.text.DefaultCaret; public class ScrollControl { JTextArea textArea; JScrollBar scrollBar; DefaultCaret caret; BoundedRangeModel model; public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new ScrollControl().makeUI(); } }); } public void makeUI() { textArea = new JTextArea(20, 30); textArea.setWrapStyleWord(true); textArea.setLineWrap(true); caret = (DefaultCaret) textArea.getCaret(); JScrollPane scrollPane = new JScrollPane(textArea); scrollBar = scrollPane.getVerticalScrollBar(); model = scrollBar.getModel(); scrollBar.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { if (model.getValue() == model.getMaximum() - model.getExtent()) { caret.setDot(textArea.getText().length()); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); } else { caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); } } }); JButton button = new JButton("Click"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for (int i = 0; i < 10; i++) { textArea.append("The quick brown fox jumps over the lazy dog. "); } } }); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.add(scrollPane, BorderLayout.CENTER); frame.add(button, BorderLayout.SOUTH); frame.setLocationRelativeTo(null); frame.setVisible(true); } }
If you're forever cleaning cobwebs, it's time to get rid of the spiders.
- 05-26-2012, 08:59 PM #7
Re: forcing scrollpane to autoscroll
Well, mine's not a text component in the sense of extending JTextComponent. It's actually a graphical component that renders text using tiles from a sprite sheet. (Very fast!) My approach could be extended to any component that wants to control its viewport position as the component's size changes. A more generally useful version of my StickyViewportLayout would consider the component's width as well as its height, and provide the option to stick to any of its sides.
The nice thing about my approach is that it happens automatically whenever the scroll pane is repainted. Also, it happens ONLY when the scroll pane is repainted; you're not creating a bunch of Runnable instances, many of which will be redundant if the component's size changes more frequently than the container is repainted.Get in the habit of using standard Java naming conventions!
Similar Threads
-
GridBagLayout - forcing all components to stay on top
By Serpenthelm in forum AWT / SwingReplies: 3Last Post: 01-05-2012, 07:52 PM -
forcing a thread to run
By yemista in forum Threads and SynchronizationReplies: 6Last Post: 11-03-2011, 07:12 PM -
How to autoscroll a JScrollPane
By Junky in forum AWT / SwingReplies: 2Last Post: 03-10-2011, 10:32 PM -
Forcing the mouse to stay quiet
By ProspectiveDeveloper in forum AWT / SwingReplies: 4Last Post: 05-03-2010, 12:17 AM -
Forcing a thread to stop
By sukatoa in forum Threads and SynchronizationReplies: 7Last Post: 07-17-2009, 07:41 AM
Bookmarks