我试图更好地理解视口,所以我制作了一个垂直时间栏,可以将其放置在JScrollPane的行标题视图中。它可以工作,但是当我进一步调查时,向下滚动时会出现,它会绘制组件中不可见的区域。我只希望它能够基于Graphics.getclipBounds()绘制可见区域,但是随着我继续向下滚动,它会绘制越来越多的组件,直到底部的打印输出表明我正在绘制组件的整个高度。
我对topMillis的计算似乎有问题,但是endMillis(基于topMillis)看起来是正确的。
要查看问题,请运行程序并注意topMillis和endMillis之间的差异在向下滚动时会增加。我希望差异保持不变,因为那应该是用户可见的区域。
在旁注中,是否有绘制该组件的更有效方法?简单的方法是每次绘制整个组件。如果时间范围太大,那将变得令人望而却步。我这样做的方式应该更高效,因为我们只绘制用户可见的内容,而不管时间范围有多大。但是我的方法直接与数据面板的大小有关,这似乎是有问题的。有没有一种方法可以使我的组件的大小与行标题视口的大小相同,但仍可以准确反映用户在滚动窗格中滚动的内容?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class TimeBarTest {
private static final int DATA_HEIGHT = 1000;
private final JScrollPane mScrollPane;
private TimeBar mRowHeaderView;
TimeBarTest() {
JPanel dataPanel = new JPanel();
dataPanel.setPreferredSize(new Dimension(0,DATA_HEIGHT));
mScrollPane = new JScrollPane(dataPanel);
mScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
mScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JButton cornerButton = new JButton("Hi");
cornerButton.addactionListener(pEvent -> {
mRowHeaderView.setTime(System.currentTimeMillis());
mScrollPane.getVerticalScrollBar().setvalue(0);
});
cornerButton.setPreferredSize(new Dimension(20,20));
JPanel columnHeader = new JPanel(new BorderLayout());
columnHeader.setPreferredSize(new Dimension(0,30));
columnHeader.setBorder(BorderFactory.createLineBorder(Color.GRAY));
columnHeader.add(new JLabel("Column Header",SwingConstants.CENTER),BorderLayout.CENTER);
mRowHeaderView = new TimeBar(DATA_HEIGHT);
mScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,cornerButton);
mScrollPane.setRowHeaderView(mRowHeaderView);
mScrollPane.setColumnHeaderView(columnHeader);
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setPreferredSize(new Dimension(500,475));
contentPane.add(mScrollPane,BorderLayout.CENTER);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String... args) {
SwingUtilities.invokeLater(() -> new TimeBarTest());
}
private class TimeBar extends JComponent {
private final int MAJOR_TICK_LENGTH = 8;
private final int MINOR_TICK_LENGTH = 4;
private final int MINUTES_PER_MAJOR = 5;
private final int MINUTES_PER_MINOR = 1;
private final long MILLIS_PER_PIXEL = 4000;
private final long MILLIS_PER_MAJOR = TimeUnit.MINUTES.toMillis(MINUTES_PER_MAJOR);
private final long MILLIS_PER_MINOR = TimeUnit.MINUTES.toMillis(MINUTES_PER_MINOR);
private final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("hh:mm");
private long mTime;
TimeBar(int pHeight) {
setPreferredSize(new Dimension(50,pHeight));
mTime = System.currentTimeMillis();
}
public void setTime(long pTime) {
mTime = pTime;
repaint();
}
@Override
protected void paintComponent(Graphics pGraphics) {
super.paintComponent(pGraphics);
pGraphics.setColor(Color.black);
Rectangle clipBounds = pGraphics.getclipBounds();
Rectangle visibleRect = getVisibleRect();
// Determine the start and end time based on the visible area.
long topMillis = mTime - (clipBounds.y * MILLIS_PER_PIXEL);
long endMillis = topMillis - ((clipBounds.y + clipBounds.height) * MILLIS_PER_PIXEL);
// Determine where we should start drawing the ticks.
long startMillis = topMillis - (topMillis % MILLIS_PER_MINOR);
System.out.println(" clipBounds=" + clipBounds);
System.out.println(" visibleRect=" + visibleRect);
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss");
System.out.println(" origTime=" + dateFormat.format(new Date(mTime)));
System.out.println(" topTime=" + dateFormat.format(new Date(topMillis)));
System.out.println(" startMillis=" + dateFormat.format(new Date(startMillis)));
System.out.println(" endTime=" + dateFormat.format(new Date(endMillis)));
System.out.println(" topMillis=" + topMillis);
System.out.println(" startMillis=" + startMillis);
System.out.println(" endMillis=" + endMillis);
System.out.println("millisPerMajor=" + MILLIS_PER_MAJOR);
System.out.println("millisPerMinor=" + MILLIS_PER_MINOR);
// Draw the ticks and labels backwards through time.
for (long i = startMillis; i >= endMillis; i -= MILLIS_PER_MINOR) {
int pixel = (int) ((topMillis - i) / MILLIS_PER_PIXEL);
System.out.println("pixel=" + pixel);
if (i % MILLIS_PER_MAJOR == 0) {
String text = mSimpleDateFormat.format(new Date(i));
pGraphics.drawString(text,1,(int) (pixel + 4));
pGraphics.drawLine(clipBounds.width,(int) pixel,clipBounds.width - MAJOR_TICK_LENGTH,(int) pixel);
} else {
pGraphics.drawLine(clipBounds.width,clipBounds.width - MINOR_TICK_LENGTH,(int) pixel);
}
}
}
}
}