如何在Java中平滑滚动JFrame

我的Java应用程序中有一个JFrame包含一个JPanel ,我在运行时创建了一些绘图对象。 问题是在滚动JFrame大图时滚动速度变慢而滚动条不能平滑移动。 请注意我正在使用Graphics 2D对象并在滚动操作上进行repaint

有没有办法平滑JFrame的滚动动作。

这是代码的一部分

 public class DiagramPanel implements MouseListener{ int click=0; Point p1; Point p2; private Dimension panelDimension; .... // variables public void go() { p1 = new Point(); p2 = new Point(); JFrame f = new JFrame(); f.setVisible(true); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.setSize(1200,500); panelx = new DiaPanel(); panelx.setOpaque(true); panelx.setBackground(Color.white); panelx.setAutoscrolls(true); panelx.addMouseListener(this); JScrollPane scrollPane = new JScrollPane(); // scrollPane.add(panelx); ClassRectangle tempRect = null; for (ClassRectangle rect : this.classRectangles) { tempRect = rect; } Rectangle rect = new Rectangle(); rect.setBounds(tempRect.getW() - 100, 0, 1000, tempLife.getEndpointY() * 500); panelDimension = new Dimension(0,0); for (ClassRectangle rectx : classRectangles){ panelDimension.width=rectx.getW()+300; } for (LifeLine life : lifeLines) { panelDimension.height=life.getEndpointY()+300; } scrollPane.setViewportView(panelx); panelx.computeVisibleRect(rect); JScrollPane scrollPane1 = new JScrollPane(panelx); panelx.setPreferredSize(panelDimension); panelx.repaint(); panelx.revalidate(); p1.x=0; p1.y=0; p2.y=panelDimension.height; p2.x=panelDimension.width; f.add( scrollPane1); scrollPane.revalidate(); f.setBackground(Color.white); } public DiagramPanel(ArrayList classRectangles, ArrayList pairs, ArrayList lines, ArrayList meth) { // constructing obj of DrawingPanel Here } public class SeqDiaPanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d2 = (Graphics2D) g; g2d2.setColor(Color.orange); //grid for (int i = 0; i < panelDimension.height; i++) { g2d2.drawLine(0, 0 + i * 5, panelDimension.width+1000, 0 + i * 5); } for (int i = 0; i < panelDimension.width; i++) { g2d2.drawLine(0 + i * 5, 0, 0 + i *5,panelDimension.height+300); } g2d2.setColor(Color.black); // objects .......... some objects here } } // draw Lines Stroke drawingStroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] { 5 }, 0); // Stroke drawingStroke = new BasicStroke(); Graphics2D g2d = (Graphics2D) g; g2d.setStroke(drawingStroke); for (Line life : lines) { g2d.drawLine(life.getStartpointX(), life.getStartpointY(), life.getEndpointX(), life.getEndpointY()); panelDimension.height=life.getEndpointY()+300; } // draw methodLfe for (Object2 ml1 : Obj2) { g2d2.fill3DRect(ml1.StartX(), ml1.getMethodStartY(), ml1.getBreadth(), ml1.getEndX(),true); } } } // tobeused public int calculateWidth(String name){ Font font = new Font("Serif", Font.BOLD, 12); FontMetrics metrics = new FontMetrics(font){ /** * */ private static final long serialVersionUID = 1L;}; int tempInt2=SwingUtilities.computeStringWidth( metrics, name); tempInt2=tempInt2+10; return tempInt2; } /*public class MouseClick implements MouseListener{ Point p = new Point(0,0); @Override public void mouseClicked(MouseEvent evnt) { px=evnt.getX(); py=evnt.getY(); System.out.println("MouseClicked @"+p.x+":"+py); } @Override public void mouseEntered(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent arg0) { // TODO Auto-generated method stub } }*/ @Override public void mouseClicked(MouseEvent evnt) { click++; if(click==1){ //Point p= new Point(); p1.x=evnt.getX(); p1.y=evnt.getY(); // System.out.println("MouseClicked1 @"+p1.x+":"+p1.y); } if(click==2){ p2.x=evnt.getX(); p2.y=evnt.getY(); //System.out.println("MouseClicked2 @"+p2.x+":"+p2.y); click=0; if(p1.x<p2.x&&p1.y<p2.y){ panelx.repaint(); } else{ } }/*else{ p1.x=0; p1.y=0; p2.x=panelDimension.width+500; p2.y=panelDimension.height+700; }*/ } @Override public void mouseEntered(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent arg0) { // TODO Auto-generated method stub } } 

为什么不将Graphics2D绘图放在(大) BufferedImage并将其显示在滚动窗格的标签中? 像这样的东西(动画,5000x5000px):

 import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.util.Random; import javax.swing.*; public class BigScrollImage { BigScrollImage() { final int x = 5000; final int y = 5000; final BufferedImage bi = new BufferedImage(x,y,BufferedImage.TYPE_INT_RGB); Graphics2D g1 = bi.createGraphics(); g1.setColor(Color.BLACK); g1.fillRect(0, 0, x, y); g1.dispose(); final JLabel label = new JLabel(new ImageIcon(bi)); ActionListener listener = new ActionListener() { Random rand = new Random(); @Override public void actionPerformed(ActionEvent ae) { Graphics2D g2 = bi.createGraphics(); int x1 = rand.nextInt(x); int x2 = rand.nextInt(x); int y1 = rand.nextInt(y); int y2 = rand.nextInt(y); int r = rand.nextInt(255); int g = rand.nextInt(255); int b = rand.nextInt(255); g2.setColor(new Color(r,g,b)); g2.drawLine(x1,y1,x2,y2); g2.dispose(); label.repaint(); } }; Timer t = new Timer(5,listener); JScrollPane scroll = new JScrollPane(label); JFrame f = new JFrame("Big Scroll"); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.add(scroll); f.pack(); f.setSize(800, 600); f.setLocationByPlatform(true); f.setVisible(true); t.start(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { new BigScrollImage(); } }); } } 

它试图每秒绘制200行,并且似乎在这里平滑滚动。

这个想法也许可以帮助你

 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.*; public class TilePainter extends JPanel implements Scrollable { private static final long serialVersionUID = 1L; public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new JFrame("Tiles"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.getContentPane().add(new JScrollPane(new TilePainter())); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } private final int TILE_SIZE = 50; private final int TILE_COUNT = 100; private final int visibleTiles = 10; private final boolean[][] loaded; private final boolean[][] loading; private final Random random; public TilePainter() { setPreferredSize(new Dimension(TILE_SIZE * TILE_COUNT, TILE_SIZE * TILE_COUNT)); loaded = new boolean[TILE_COUNT][TILE_COUNT]; loading = new boolean[TILE_COUNT][TILE_COUNT]; random = new Random(); } public boolean getTile(final int x, final int y) { boolean canPaint = loaded[x][y]; if (!canPaint && !loading[x][y]) { loading[x][y] = true; Timer timer = new Timer(random.nextInt(500), new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loaded[x][y] = true; repaint(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE); } }); timer.setRepeats(false); timer.start(); } return canPaint; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Rectangle clip = g.getClipBounds(); int startX = clip.x - (clip.x % TILE_SIZE); int startY = clip.y - (clip.y % TILE_SIZE); for (int x = startX; x < clip.x + clip.width; x += TILE_SIZE) { for (int y = startY; y < clip.y + clip.height; y += TILE_SIZE) { if (getTile(x / TILE_SIZE, y / TILE_SIZE)) { g.setColor(Color.GREEN); } else { g.setColor(Color.RED); } g.fillRect(x, y, TILE_SIZE - 1, TILE_SIZE - 1); } } } @Override public Dimension getPreferredScrollableViewportSize() { return new Dimension(visibleTiles * TILE_SIZE, visibleTiles * TILE_SIZE); } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return TILE_SIZE * Math.max(1, visibleTiles - 1); } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public boolean getScrollableTracksViewportWidth() { return false; } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return TILE_SIZE; } } 

第1部分

感谢他和Gilbert Le Blanc,mKorbel的答案有我的小改编:

 import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.lang.ref.WeakReference; import java.util.Random; import javax.swing.Scrollable; import javax.swing.SwingConstants; import javax.swing.Timer; /** * * @author leBenj */ public class GJPanelBufferedImageTileAdapter extends GJPanelBufferedImageAdapter implements Scrollable { protected BufferedImage _image = null; protected GIPanelListener _parent = null; private int TILE_SIZE_W = -1; private int TILE_SIZE_H = -1; private int TILE_COUNT_W = 32; private int TILE_COUNT_H = 32; private int visibleTiles = 10; private boolean[][] loading; private WeakReference[][] subs; private final Random random; public GJPanelBufferedImageTileAdapter( final GIPanelListener parent , LayoutManager layout , boolean isDoubleBuffered ) { super( parent , layout , isDoubleBuffered ); this._parent = parent; resetTiling(); random = new Random(); } public void resetTiling() { loading = new boolean[TILE_COUNT_W][TILE_COUNT_H]; subs = new WeakReference[TILE_COUNT_W][TILE_COUNT_H]; } private BufferedImage getTile( int x , int y ) { BufferedImage retour = null; if( x < TILE_COUNT_W ) { if( y < TILE_COUNT_H ) { if( subs[x][y] != null ) { retour = subs[x][y].get(); } } } return retour; } private void setTile( BufferedImage sub , int x , int y ) { subs[x][y] = new WeakReference( sub ); } private boolean loadTile( final int x , final int y ) { boolean canPaint = ( getTile( x , y ) != null ); if( x < TILE_COUNT_W ) { if( y < TILE_COUNT_H ) { if( !canPaint && !loading[x][y] ) { Timer timer = new Timer( random.nextInt( 500 ) , new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { BufferedImage sub = _image.getSubimage( x * TILE_SIZE_W , y * TILE_SIZE_H , TILE_SIZE_W , TILE_SIZE_H ); setTile( sub , x , y ); repaint( x * TILE_SIZE_W , y * TILE_SIZE_H , TILE_SIZE_W , TILE_SIZE_H ); } } ); timer.setRepeats( false ); timer.start(); } } } return canPaint; } // using paint(g) instead of paintComponent(g) to start drawing as soon as the panel is ready @Override protected void paint( Graphics g ) { super.paint( g ); Rectangle clip = g.getClipBounds(); int startX = clip.x - ( clip.x % TILE_SIZE_W ); int startY = clip.y - ( clip.y % TILE_SIZE_H ); int endX = clip.x + clip.width /*- TILE_SIZE_W*/; int endY = clip.y + clip.height /*- TILE_SIZE_H*/; for( int x = startX ; x < endX ; x += TILE_SIZE_W ) { for( int y = startY ; y < endY ; y += TILE_SIZE_H ) { if( loadTile( x / TILE_SIZE_W , y / TILE_SIZE_H ) ) { BufferedImage tile = getTile( x / TILE_SIZE_W , y / TILE_SIZE_H ); if( tile != null ) { g.drawImage( subs[x / TILE_SIZE_W][y / TILE_SIZE_H].get() , x , y , this ); } } else { g.setColor( Color.RED ); g.fillRect( x , y , TILE_SIZE_W - 1 , TILE_SIZE_H - 1 ); } } } g.dispose(); // Without this, the original view area will never be painted } /** * @param image the _image to set */ public void setImage( BufferedImage image ) { this._image = image; TILE_SIZE_W = _image.getWidth() / TILE_COUNT_W; TILE_SIZE_H = _image.getHeight() / TILE_COUNT_H; setPreferredSize( new Dimension( TILE_SIZE_W * TILE_COUNT_W , TILE_SIZE_H * TILE_COUNT_H ) ); } @Override public Dimension getPreferredScrollableViewportSize() { return new Dimension( visibleTiles * TILE_SIZE_W , visibleTiles * TILE_SIZE_H ); } @Override public int getScrollableBlockIncrement( Rectangle visibleRect , int orientation , int direction ) { if( orientation == SwingConstants.HORIZONTAL ) { return TILE_SIZE_W * Math.max( 1 , visibleTiles - 1 ); } else { return TILE_SIZE_H * Math.max( 1 , visibleTiles - 1 ); } } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public boolean getScrollableTracksViewportWidth() { return false; } @Override public int getScrollableUnitIncrement( Rectangle visibleRect , int orientation , int direction ) { if( orientation == SwingConstants.HORIZONTAL ) { return TILE_SIZE_W; } else { return TILE_SIZE_H; } } } 

解释:

右侧和底部滚动有一点问题:为了避免ArrayOutOfBoundsException,我在x和y上实现了测试。

我将TILE_SIZE两部分,以适应图像比例。

正如我在上一篇评论中的链接中所提到的,将tile与WeakReference数组耦合可以调节内存使用情况:我替换了WeakReference[][] boolean[][] loadedboolean[][] loaded WeakReference[][]并实现了tileGet(x,y)函数来获取瓷砖。

setImage()方法初始化类字段,例如tile的大小。

this._image字段inheritance自超类,实现如下:

 protected BufferedImage _image = null; 

我希望这可以帮助别人。